2 * "$Id: ipp.c,v 1.38.2.33 2004/06/29 03:18:10 mike Exp $"
4 * IPP backend for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2003 by Easy Software Products, all rights reserved.
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
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636-3111 USA
20 * Voice: (301) 373-9603
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
24 * This file is subject to the Apple OS-Developed Software exception.
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
35 * sigterm_handler() - Handle 'terminate' signals that stop the backend.
39 * Include necessary headers.
45 #include <sys/types.h>
47 #include <cups/http-private.h>
48 #include <cups/cups.h>
49 #include <cups/language.h>
50 #include <cups/string.h>
59 static char *password
= NULL
; /* Password for device URI */
61 static char pstmpname
[1024] = ""; /* Temporary PostScript file name */
62 #endif /* __APPLE__ */
63 static char tmpfilename
[1024] = ""; /* Temporary spool file name */
70 void check_printer_state(http_t
*http
, cups_lang_t
*language
,
71 const char *charset
, const char *uri
, /* I - Printer URI */
72 const char *resource
, const char *user
,
74 const char *password_cb(const char *);
75 int report_printer_state(ipp_t
*ipp
);
78 int run_pictwps_filter(char **argv
, const char *filename
);
79 #endif /* __APPLE__ */
80 static void sigterm_handler(int sig
);
84 * 'main()' - Send a file to the printer or server.
88 * printer-uri job-id user title copies options [file]
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 */
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
*charset_sup
; /* charset-supported attribute */
122 ipp_attribute_t
*format_sup
; /* document-format-supported attribute */
123 ipp_attribute_t
*printer_state
;
124 /* printer-state attribute */
125 ipp_attribute_t
*printer_accepting
;
126 /* printer-is-accepting-jobs attribute */
127 const char *charset
; /* Character set to use */
128 cups_lang_t
*language
; /* Default language */
129 int copies
; /* Number of copies remaining */
130 const char *content_type
; /* CONTENT_TYPE environment variable */
131 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
132 struct sigaction action
; /* Actions for POSIX signals */
133 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
134 int version
; /* IPP version */
135 int reasons
; /* Number of printer-state-reasons shown */
136 static const char * const pattrs
[] =
137 { /* Printer attributes we want */
140 "document-format-supported",
141 "printer-is-accepting-jobs",
143 "printer-state-reasons",
145 static const char * const jattrs
[] =
146 { /* Job attributes we want */
147 "job-media-sheets-completed",
153 * Make sure status messages are not buffered...
156 setbuf(stderr
, NULL
);
159 * Ignore SIGPIPE and catch SIGTERM signals...
163 sigset(SIGPIPE
, SIG_IGN
);
164 sigset(SIGTERM
, sigterm_handler
);
165 #elif defined(HAVE_SIGACTION)
166 memset(&action
, 0, sizeof(action
));
167 action
.sa_handler
= SIG_IGN
;
168 sigaction(SIGPIPE
, &action
, NULL
);
170 sigemptyset(&action
.sa_mask
);
171 sigaddset(&action
.sa_mask
, SIGTERM
);
172 action
.sa_handler
= sigterm_handler
;
173 sigaction(SIGTERM
, &action
, NULL
);
175 signal(SIGPIPE
, SIG_IGN
);
176 signal(SIGTERM
, sigterm_handler
);
177 #endif /* HAVE_SIGSET */
180 * Check command-line...
187 if ((s
= strrchr(argv
[0], '/')) != NULL
)
192 printf("network %s \"Unknown\" \"Internet Printing Protocol (%s)\"\n", s
, s
);
195 else if (argc
< 6 || argc
> 7)
197 fprintf(stderr
, "Usage: %s job-id user title copies options [file]\n",
203 * Get the content type...
207 content_type
= getenv("CONTENT_TYPE");
209 content_type
= "application/vnd.cups-raw";
211 if (content_type
== NULL
)
212 content_type
= "application/octet-stream";
215 * Extract the hostname and printer name from the URI...
218 if (strchr(argv
[0], ':') != NULL
)
219 httpSeparate(argv
[0], method
, username
, hostname
, &port
, resource
);
220 else if (getenv("DEVICE_URI") != NULL
)
221 httpSeparate(getenv("DEVICE_URI"), method
, username
, hostname
, &port
,
225 fputs("ERROR: Missing device URI on command-line and no DEVICE_URI environment variable!\n",
230 if (!strcmp(method
, "https"))
231 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS
);
234 * If we have 7 arguments, print the file named on the command-line.
235 * Otherwise, copy stdin to a temporary file and print the temporary
242 * Copy stdin to a temporary file...
245 int fd
; /* Temporary file */
246 char buffer
[8192]; /* Buffer for copying */
247 int bytes
; /* Number of bytes read */
250 if ((fd
= cupsTempFd(tmpfilename
, sizeof(tmpfilename
))) < 0)
252 perror("ERROR: unable to create temporary file");
256 while ((bytes
= fread(buffer
, 1, sizeof(buffer
), stdin
)) > 0)
257 if (write(fd
, buffer
, bytes
) < bytes
)
259 perror("ERROR: unable to write to temporary file");
266 filename
= tmpfilename
;
272 * See if there are any options...
279 if ((optptr
= strchr(resource
, '?')) != NULL
)
282 * Yup, terminate the device name string and move to the first
283 * character of the optptr...
289 * Then parse the optptr...
298 for (ptr
= name
; *optptr
&& *optptr
!= '=';)
299 if (ptr
< (name
+ sizeof(name
) - 1))
311 for (ptr
= value
; *optptr
&& *optptr
!= '+';)
312 if (ptr
< (value
+ sizeof(value
) - 1))
323 * Process the option...
326 if (!strcasecmp(name
, "waitjob"))
329 * Wait for job completion?
332 waitjob
= !strcasecmp(value
, "on") ||
333 !strcasecmp(value
, "yes") ||
334 !strcasecmp(value
, "true");
336 else if (!strcasecmp(name
, "waitprinter"))
339 * Wait for printer idle?
342 waitprinter
= !strcasecmp(value
, "on") ||
343 !strcasecmp(value
, "yes") ||
344 !strcasecmp(value
, "true");
346 else if (!strcasecmp(name
, "encryption"))
349 * Enable/disable encryption?
352 if (!strcasecmp(value
, "always"))
353 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS
);
354 else if (!strcasecmp(value
, "required"))
355 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED
);
356 else if (!strcasecmp(value
, "never"))
357 cupsSetEncryption(HTTP_ENCRYPT_NEVER
);
358 else if (!strcasecmp(value
, "ifrequested"))
359 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED
);
362 fprintf(stderr
, "ERROR: Unknown encryption option value \"%s\"!\n",
366 else if (!strcasecmp(name
, "version"))
368 if (!strcmp(value
, "1.0"))
370 else if (!strcmp(value
, "1.1"))
374 fprintf(stderr
, "ERROR: Unknown version option value \"%s\"!\n",
384 fprintf(stderr
, "ERROR: Unknown option \"%s\" with value \"%s\"!\n",
391 * Set the authentication info, if any...
394 cupsSetPasswordCB(password_cb
);
398 if ((password
= strchr(username
, ':')) != NULL
)
401 cupsSetUser(username
);
405 * Try connecting to the remote server...
410 fprintf(stderr
, "INFO: Connecting to %s on port %d...\n", hostname
, port
);
412 if ((http
= httpConnectEncrypt(hostname
, port
, cupsEncryption())) == NULL
)
414 if (getenv("CLASS") != NULL
)
417 * If the CLASS environment variable is set, the job was submitted
418 * to a class and not to a specific queue. In this case, we want
419 * to abort immediately so that the job can be requeued on the next
420 * available printer in the class.
423 fprintf(stderr
, "INFO: Unable to connect to %s, queuing on next printer in class...\n",
426 if (argc
== 6 || strcmp(filename
, argv
[6]))
430 * Sleep 5 seconds to keep the job from requeuing too rapidly...
438 if (errno
== ECONNREFUSED
|| errno
== EHOSTDOWN
||
439 errno
== EHOSTUNREACH
)
441 fprintf(stderr
, "INFO: Network host \'%s\' is busy; will retry in 30 seconds...",
447 fprintf(stderr
, "INFO: Unable to lookup host \'%s\' - %s\n",
448 hostname
, hstrerror(h_errno
));
453 perror("ERROR: Unable to connect to IPP host");
458 while (http
== NULL
);
460 fprintf(stderr
, "INFO: Connected to %s...\n", hostname
);
463 * Build a URI for the printer and fill the standard IPP attributes for
464 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
465 * might contain username:password information...
468 snprintf(uri
, sizeof(uri
), "%s://%s:%d%s", method
, hostname
, port
, resource
);
471 * First validate the destination and see if the device supports multiple
472 * copies. We have to do this because some IPP servers (e.g. HP JetDirect)
473 * don't support the copies attribute...
476 language
= cupsLangDefault();
485 * Build the IPP request...
489 request
->request
.op
.version
[1] = version
;
490 request
->request
.op
.operation_id
= IPP_GET_PRINTER_ATTRIBUTES
;
491 request
->request
.op
.request_id
= 1;
493 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
494 "attributes-charset", NULL
, "utf-8");
496 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
497 "attributes-natural-language", NULL
,
498 language
!= NULL
? language
->language
: "en");
500 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
503 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
504 "requested-attributes", sizeof(pattrs
) / sizeof(pattrs
[0]),
511 fputs("DEBUG: Getting supported attributes...\n", stderr
);
513 if ((supported
= cupsDoRequest(http
, request
, resource
)) == NULL
)
514 ipp_status
= cupsLastError();
516 ipp_status
= supported
->request
.status
.status_code
;
518 if (ipp_status
> IPP_OK_CONFLICT
)
520 if (ipp_status
== IPP_PRINTER_BUSY
||
521 ipp_status
== IPP_SERVICE_UNAVAILABLE
)
523 fputs("INFO: Printer busy; will retry in 10 seconds...\n", stderr
);
524 report_printer_state(supported
);
527 else if ((ipp_status
== IPP_BAD_REQUEST
||
528 ipp_status
== IPP_VERSION_NOT_SUPPORTED
) && version
== 1)
531 * Switch to IPP/1.0...
534 fputs("INFO: Printer does not support IPP/1.1, trying IPP/1.0...\n", stderr
);
538 else if (ipp_status
== IPP_NOT_FOUND
)
540 fputs("ERROR: Destination printer does not exist!\n", stderr
);
543 ippDelete(supported
);
549 fprintf(stderr
, "ERROR: Unable to get printer status (%s)!\n",
550 ippErrorString(ipp_status
));
555 ippDelete(supported
);
559 else if ((copies_sup
= ippFindAttribute(supported
, "copies-supported",
560 IPP_TAG_RANGE
)) != NULL
)
563 * Has the "copies-supported" attribute - does it have an upper
567 if (copies_sup
->values
[0].range
.upper
<= 1)
568 copies_sup
= NULL
; /* No */
571 charset_sup
= ippFindAttribute(supported
, "charset-supported",
573 format_sup
= ippFindAttribute(supported
, "document-format-supported",
578 fprintf(stderr
, "DEBUG: document-format-supported (%d values)\n",
579 format_sup
->num_values
);
580 for (i
= 0; i
< format_sup
->num_values
; i
++)
581 fprintf(stderr
, "DEBUG: [%d] = \"%s\"\n", i
,
582 format_sup
->values
[i
].string
.text
);
585 report_printer_state(supported
);
587 while (ipp_status
> IPP_OK_CONFLICT
);
590 * See if the printer is accepting jobs and is not stopped; if either
591 * condition is true and we are printing to a class, requeue the job...
594 if (getenv("CLASS") != NULL
)
596 printer_state
= ippFindAttribute(supported
, "printer-state",
598 printer_accepting
= ippFindAttribute(supported
, "printer-is-accepting-jobs",
601 if (printer_state
== NULL
||
602 (printer_state
->values
[0].integer
> IPP_PRINTER_PROCESSING
&& waitprinter
) ||
603 printer_accepting
== NULL
||
604 !printer_accepting
->values
[0].boolean
)
607 * If the CLASS environment variable is set, the job was submitted
608 * to a class and not to a specific queue. In this case, we want
609 * to abort immediately so that the job can be requeued on the next
610 * available printer in the class.
613 fprintf(stderr
, "INFO: Unable to queue job on %s, queuing on next printer in class...\n",
616 ippDelete(supported
);
619 if (argc
== 6 || strcmp(filename
, argv
[6]))
623 * Sleep 5 seconds to keep the job from requeuing too rapidly...
633 * See if the printer supports multiple copies...
636 if (copies_sup
|| argc
< 7)
639 copies
= atoi(argv
[4]);
642 * Figure out the character set to use...
645 charset
= language
? cupsLangEncoding(language
) : "us-ascii";
650 * See if IPP server supports the requested character set...
653 for (i
= 0; i
< charset_sup
->num_values
; i
++)
654 if (strcasecmp(charset
, charset_sup
->values
[i
].string
.text
) == 0)
658 * If not, choose us-ascii or utf-8...
661 if (i
>= charset_sup
->num_values
)
664 * See if us-ascii is supported...
667 for (i
= 0; i
< charset_sup
->num_values
; i
++)
668 if (strcasecmp("us-ascii", charset_sup
->values
[i
].string
.text
) == 0)
671 if (i
< charset_sup
->num_values
)
672 charset
= "us-ascii";
679 * Then issue the print-job request...
687 * Build the IPP request...
691 request
->request
.op
.version
[1] = version
;
692 request
->request
.op
.operation_id
= IPP_PRINT_JOB
;
693 request
->request
.op
.request_id
= 1;
695 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
696 "attributes-charset", NULL
, charset
);
698 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
699 "attributes-natural-language", NULL
,
700 language
!= NULL
? language
->language
: "en");
702 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
705 fprintf(stderr
, "DEBUG: printer-uri = \"%s\"\n", uri
);
708 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
709 "requesting-user-name", NULL
, argv
[2]);
711 fprintf(stderr
, "DEBUG: requesting-user-name = \"%s\"\n", argv
[2]);
714 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "job-name", NULL
,
717 fprintf(stderr
, "DEBUG: job-name = \"%s\"\n", argv
[3]);
720 * Handle options on the command-line...
724 num_options
= cupsParseOptions(argv
[5], 0, &options
);
727 if (content_type
!= NULL
&& strcasecmp(content_type
, "application/pictwps") == 0)
729 if (format_sup
!= NULL
)
731 for (i
= 0; i
< format_sup
->num_values
; i
++)
732 if (strcasecmp(content_type
, format_sup
->values
[i
].string
.text
) == 0)
736 if (format_sup
== NULL
|| i
>= format_sup
->num_values
)
739 * Remote doesn't support "application/pictwps" (i.e. it's not MacOS X)
740 * so convert the document to PostScript...
743 if (run_pictwps_filter(argv
, filename
))
746 filename
= pstmpname
;
749 * Change the MIME type to application/postscript...
752 content_type
= "application/postscript";
755 #endif /* __APPLE__ */
757 if (content_type
!= NULL
&& format_sup
!= NULL
)
759 for (i
= 0; i
< format_sup
->num_values
; i
++)
760 if (strcasecmp(content_type
, format_sup
->values
[i
].string
.text
) == 0)
763 if (i
< format_sup
->num_values
)
764 num_options
= cupsAddOption("document-format", content_type
,
765 num_options
, &options
);
771 * Only send options if the destination printer supports the copies
772 * attribute. This is a hack for the HP JetDirect implementation of
773 * IPP, which does not accept extension attributes and incorrectly
774 * reports a client-error-bad-request error instead of the
775 * successful-ok-unsupported-attributes status. In short, at least
776 * some HP implementations of IPP are non-compliant.
779 cupsEncodeOptions(request
, num_options
, options
);
780 ippAddInteger(request
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "copies",
784 cupsFreeOptions(num_options
, options
);
787 * If copies aren't supported, then we are likely dealing with an HP
788 * JetDirect. The HP IPP implementation seems to close the connection
789 * after every request (that is, it does *not* implement HTTP Keep-
790 * Alive, which is REQUIRED by HTTP/1.1...
800 if ((response
= cupsDoFileRequest(http
, request
, resource
, filename
)) == NULL
)
801 ipp_status
= cupsLastError();
803 ipp_status
= response
->request
.status
.status_code
;
805 if (ipp_status
> IPP_OK_CONFLICT
)
809 if (ipp_status
== IPP_SERVICE_UNAVAILABLE
||
810 ipp_status
== IPP_PRINTER_BUSY
)
812 fputs("INFO: Printer is busy; retrying print job...\n", stderr
);
816 fprintf(stderr
, "ERROR: Print file was not accepted (%s)!\n",
817 ippErrorString(ipp_status
));
819 else if ((job_id_attr
= ippFindAttribute(response
, "job-id",
820 IPP_TAG_INTEGER
)) == NULL
)
822 fputs("INFO: Print file accepted - job ID unknown.\n", stderr
);
827 job_id
= job_id_attr
->values
[0].integer
;
828 fprintf(stderr
, "INFO: Print file accepted - job ID %d.\n", job_id
);
834 if (ipp_status
<= IPP_OK_CONFLICT
&& argc
> 6)
836 fprintf(stderr
, "PAGE: 1 %d\n", copies_sup
? atoi(argv
[4]) : 1);
839 else if (ipp_status
!= IPP_SERVICE_UNAVAILABLE
&&
840 ipp_status
!= IPP_PRINTER_BUSY
)
844 * Wait for the job to complete...
847 if (!job_id
|| !waitjob
)
850 fputs("INFO: Waiting for job to complete...\n", stderr
);
855 * Build an IPP_GET_JOB_ATTRIBUTES request...
859 request
->request
.op
.version
[1] = version
;
860 request
->request
.op
.operation_id
= IPP_GET_JOB_ATTRIBUTES
;
861 request
->request
.op
.request_id
= 1;
863 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
864 "attributes-charset", NULL
, charset
);
866 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
867 "attributes-natural-language", NULL
,
868 language
!= NULL
? language
->language
: "en");
870 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
873 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id",
877 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
878 "requesting-user-name", NULL
, argv
[2]);
880 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
881 "requested-attributes", sizeof(jattrs
) / sizeof(jattrs
[0]),
891 if ((response
= cupsDoRequest(http
, request
, resource
)) == NULL
)
892 ipp_status
= cupsLastError();
894 ipp_status
= response
->request
.status
.status_code
;
896 if (ipp_status
== IPP_NOT_FOUND
)
899 * Job has gone away and/or the server has no job history...
908 if (ipp_status
> IPP_OK_CONFLICT
)
910 if (ipp_status
!= IPP_SERVICE_UNAVAILABLE
&&
911 ipp_status
!= IPP_PRINTER_BUSY
)
916 fprintf(stderr
, "ERROR: Unable to get job %d attributes (%s)!\n",
917 job_id
, ippErrorString(ipp_status
));
922 if (response
!= NULL
)
924 if ((job_sheets
= ippFindAttribute(response
, "job-media-sheets-completed",
925 IPP_TAG_INTEGER
)) != NULL
)
926 fprintf(stderr
, "PAGE: total %d\n", job_sheets
->values
[0].integer
);
928 if ((job_state
= ippFindAttribute(response
, "job-state",
929 IPP_TAG_ENUM
)) != NULL
)
932 * Stop polling if the job is finished or pending-held...
935 if (job_state
->values
[0].integer
> IPP_JOB_PROCESSING
||
936 job_state
->values
[0].integer
== IPP_JOB_HELD
)
948 * Check the printer state and report it if necessary...
952 httpReconnect(http);*/
954 check_printer_state(http
, language
, charset
, uri
, resource
, argv
[2],
958 * Wait 10 seconds before polling again...
966 * Check the printer state and report it if necessary...
970 httpReconnect(http);*/
972 check_printer_state(http
, language
, charset
, uri
, resource
, argv
[2], version
);
981 ippDelete(supported
);
984 * Remove the temporary file(s) if necessary...
993 #endif /* __APPLE__ */
996 * Return the queue status...
999 return (ipp_status
> IPP_OK_CONFLICT
);
1004 * 'check_printer_state()' - Check the printer state...
1008 check_printer_state(http_t
*http
, /* I - HTTP connection */
1009 cups_lang_t
*language
,
1011 const char *charset
,
1013 const char *uri
, /* I - Printer URI */
1014 const char *resource
,
1015 /* I - Resource path */
1016 const char *user
, /* I - Username, if any */
1017 int version
)/* I - IPP version */
1019 ipp_t
*request
, /* IPP request */
1020 *response
; /* IPP response */
1024 * Check on the printer state...
1028 request
->request
.op
.version
[1] = version
;
1029 request
->request
.op
.operation_id
= IPP_GET_PRINTER_ATTRIBUTES
;
1030 request
->request
.op
.request_id
= 1;
1032 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
1033 "attributes-charset", NULL
, charset
);
1035 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
1036 "attributes-natural-language", NULL
,
1037 language
!= NULL
? language
->language
: "en");
1039 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1042 if (user
&& user
[0])
1043 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1044 "requesting-user-name", NULL
, user
);
1046 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1047 "requested-attributes", NULL
, "printer-state-reasons");
1053 if ((response
= cupsDoRequest(http
, request
, resource
)) != NULL
)
1055 report_printer_state(response
);
1056 ippDelete(response
);
1062 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
1065 const char * /* O - Password */
1066 password_cb(const char *prompt
) /* I - Prompt (not used) */
1075 * 'report_printer_state()' - Report the printer state.
1078 int /* O - Number of reasons shown */
1079 report_printer_state(ipp_t
*ipp
) /* I - IPP response */
1081 int i
; /* Looping var */
1082 int count
; /* Count of reasons shown... */
1083 ipp_attribute_t
*reasons
; /* printer-state-reasons */
1084 const char *reason
; /* Current reason */
1085 const char *message
; /* Message to show */
1086 char unknown
[1024]; /* Unknown message string */
1087 const char *prefix
; /* Prefix for STATE: line */
1088 char state
[1024]; /* State string */
1091 if ((reasons
= ippFindAttribute(ipp
, "printer-state-reasons",
1092 IPP_TAG_KEYWORD
)) == NULL
)
1098 for (i
= 0, count
= 0; i
< reasons
->num_values
; i
++)
1100 reason
= reasons
->values
[i
].string
.text
;
1102 strlcat(state
, prefix
, sizeof(state
));
1103 strlcat(state
, reason
, sizeof(state
));
1108 if (strncmp(reason
, "media-needed", 12) == 0)
1109 message
= "Media tray needs to be filled.";
1110 else if (strncmp(reason
, "media-jam", 9) == 0)
1111 message
= "Media jam!";
1112 else if (strncmp(reason
, "moving-to-paused", 16) == 0 ||
1113 strncmp(reason
, "paused", 6) == 0 ||
1114 strncmp(reason
, "shutdown", 8) == 0)
1115 message
= "Printer off-line.";
1116 else if (strncmp(reason
, "toner-low", 9) == 0)
1117 message
= "Toner low.";
1118 else if (strncmp(reason
, "toner-empty", 11) == 0)
1119 message
= "Out of toner!";
1120 else if (strncmp(reason
, "cover-open", 10) == 0)
1121 message
= "Cover open.";
1122 else if (strncmp(reason
, "interlock-open", 14) == 0)
1123 message
= "Interlock open.";
1124 else if (strncmp(reason
, "door-open", 9) == 0)
1125 message
= "Door open.";
1126 else if (strncmp(reason
, "input-tray-missing", 18) == 0)
1127 message
= "Media tray missing!";
1128 else if (strncmp(reason
, "media-low", 9) == 0)
1129 message
= "Media tray almost empty.";
1130 else if (strncmp(reason
, "media-empty", 11) == 0)
1131 message
= "Media tray empty!";
1132 else if (strncmp(reason
, "output-tray-missing", 19) == 0)
1133 message
= "Output tray missing!";
1134 else if (strncmp(reason
, "output-area-almost-full", 23) == 0)
1135 message
= "Output bin almost full.";
1136 else if (strncmp(reason
, "output-area-full", 16) == 0)
1137 message
= "Output bin full!";
1138 else if (strncmp(reason
, "marker-supply-low", 17) == 0)
1139 message
= "Ink/toner almost empty.";
1140 else if (strncmp(reason
, "marker-supply-empty", 19) == 0)
1141 message
= "Ink/toner empty!";
1142 else if (strncmp(reason
, "marker-waste-almost-full", 24) == 0)
1143 message
= "Ink/toner waste bin almost full.";
1144 else if (strncmp(reason
, "marker-waste-full", 17) == 0)
1145 message
= "Ink/toner waste bin full!";
1146 else if (strncmp(reason
, "fuser-over-temp", 15) == 0)
1147 message
= "Fuser temperature high!";
1148 else if (strncmp(reason
, "fuser-under-temp", 16) == 0)
1149 message
= "Fuser temperature low!";
1150 else if (strncmp(reason
, "opc-near-eol", 12) == 0)
1151 message
= "OPC almost at end-of-life.";
1152 else if (strncmp(reason
, "opc-life-over", 13) == 0)
1153 message
= "OPC at end-of-life!";
1154 else if (strncmp(reason
, "developer-low", 13) == 0)
1155 message
= "Developer almost empty.";
1156 else if (strncmp(reason
, "developer-empty", 15) == 0)
1157 message
= "Developer empty!";
1158 else if (strstr(reason
, "error") != NULL
)
1162 snprintf(unknown
, sizeof(unknown
), "Unknown printer error (%s)!",
1169 if (strstr(reasons
->values
[i
].string
.text
, "error"))
1170 fprintf(stderr
, "ERROR: %s\n", message
);
1171 else if (strstr(reasons
->values
[i
].string
.text
, "warning"))
1172 fprintf(stderr
, "WARNING: %s\n", message
);
1174 fprintf(stderr
, "INFO: %s\n", message
);
1178 fprintf(stderr
, "%s\n", state
);
1186 * 'run_pictwps_filter()' - Convert PICT files to PostScript when printing
1189 * This step is required because the PICT format is not documented and
1190 * subject to change, so developing a filter for other OS's is infeasible.
1191 * Also, fonts required by the PICT file need to be embedded on the
1192 * client side (which has the fonts), so we run the filter to get a
1193 * PostScript file for printing...
1196 int /* O - Exit status of filter */
1197 run_pictwps_filter(char **argv
, /* I - Command-line arguments */
1198 const char *filename
) /* I - Filename */
1200 struct stat fileinfo
; /* Print file information */
1201 const char *ppdfile
; /* PPD file for destination printer */
1202 int pid
; /* Child process ID */
1203 int fd
; /* Temporary file descriptor */
1204 int status
; /* Exit status of filter */
1205 const char *printer
; /* PRINTER env var */
1206 static char ppdenv
[1024]; /* PPD environment variable */
1210 * First get the PPD file for the printer...
1213 printer
= getenv("PRINTER");
1216 fputs("ERROR: PRINTER environment variable not defined!\n", stderr
);
1220 if ((ppdfile
= cupsGetPPD(printer
)) == NULL
)
1222 fprintf(stderr
, "ERROR: Unable to get PPD file for printer \"%s\" - %s.\n",
1223 printer
, ippErrorString(cupsLastError()));
1228 snprintf(ppdenv
, sizeof(ppdenv
), "PPD=%s", ppdfile
);
1233 * Then create a temporary file for printing...
1236 if ((fd
= cupsTempFd(pstmpname
, sizeof(pstmpname
))) < 0)
1238 fprintf(stderr
, "ERROR: Unable to create temporary file - %s.\n",
1246 * Get the owner of the spool file - it is owned by the user we want to run
1251 stat(argv
[6], &fileinfo
);
1255 * Use the OSX defaults, as an up-stream filter created the PICT
1259 fileinfo
.st_uid
= 1;
1260 fileinfo
.st_gid
= 80;
1264 chown(ppdfile
, fileinfo
.st_uid
, fileinfo
.st_gid
);
1266 fchown(fd
, fileinfo
.st_uid
, fileinfo
.st_gid
);
1269 * Finally, run the filter to convert the file...
1272 if ((pid
= fork()) == 0)
1275 * Child process for pictwpstops... Redirect output of pictwpstops to a
1286 * Change to an unpriviledged user...
1289 setgid(fileinfo
.st_gid
);
1290 setuid(fileinfo
.st_uid
);
1293 execlp("pictwpstops", printer
, argv
[1], argv
[2], argv
[3], argv
[4], argv
[5],
1295 perror("ERROR: Unable to exec pictwpstops");
1307 perror("ERROR: Unable to fork pictwpstops");
1315 * Now wait for the filter to complete...
1318 if (wait(&status
) < 0)
1320 perror("ERROR: Unable to wait for pictwpstops");
1336 fprintf(stderr
, "ERROR: pictwpstops exited with status %d!\n",
1339 fprintf(stderr
, "ERROR: pictwpstops exited on signal %d!\n",
1347 * Return with no errors..
1352 #endif /* __APPLE__ */
1356 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
1360 sigterm_handler(int sig
) /* I - Signal */
1362 (void)sig
; /* remove compiler warnings... */
1365 * Remove the temporary file(s) if necessary...
1369 unlink(tmpfilename
);
1374 #endif /* __APPLE__ */
1381 * End of "$Id: ipp.c,v 1.38.2.33 2004/06/29 03:18:10 mike Exp $".