4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 /* $Id: ipp-support.c 148 2006-04-25 16:54:17Z njacobs $ */
31 #include <papi_impl.h>
40 #include <config-site.h>
44 static void ipp_add_printer_uri(service_t
*svc
, char *name
,
45 papi_attribute_t
***op
);
48 http_to_papi_status(http_status_t status
)
53 case HTTP_BAD_REQUEST
:
54 return (PAPI_BAD_REQUEST
);
55 case HTTP_UNAUTHORIZED
:
57 return (PAPI_NOT_AUTHORIZED
);
59 return (PAPI_NOT_FOUND
);
62 case HTTP_SERVICE_UNAVAILABLE
:
63 return (PAPI_SERVICE_UNAVAILABLE
);
65 return ((papi_status_t
)status
);
70 ipp_to_papi_status(uint16_t status
)
75 case IPP_OK_IGNORED_ATTRIBUTES
:
77 case IPP_OK_CONFLICTING_ATTRIBUTES
:
79 case IPP_OK_IGNORED_SUBSCRIPTIONS
:
80 return (PAPI_OK_IGNORED_SUBSCRIPTIONS
);
81 case IPP_OK_IGNORED_NOTIFICATIONS
:
82 return (PAPI_OK_IGNORED_NOTIFICATIONS
);
83 case IPP_CERR_BAD_REQUEST
:
84 return (PAPI_BAD_REQUEST
);
85 case IPP_CERR_FORBIDDEN
:
86 return (PAPI_FORBIDDEN
);
87 case IPP_CERR_NOT_AUTHENTICATED
:
88 return (PAPI_NOT_AUTHENTICATED
);
89 case IPP_CERR_NOT_AUTHORIZED
:
90 return (PAPI_NOT_AUTHORIZED
);
91 case IPP_CERR_NOT_POSSIBLE
:
92 return (PAPI_NOT_POSSIBLE
);
93 case IPP_CERR_TIMEOUT
:
94 return (PAPI_TIMEOUT
);
95 case IPP_CERR_NOT_FOUND
:
96 return (PAPI_NOT_FOUND
);
99 case IPP_CERR_REQUEST_ENTITY
:
100 return (PAPI_REQUEST_ENTITY
);
101 case IPP_CERR_REQUEST_VALUE
:
102 return (PAPI_REQUEST_VALUE
);
103 case IPP_CERR_DOCUMENT_FORMAT
:
104 return (PAPI_DOCUMENT_FORMAT
);
105 case IPP_CERR_ATTRIBUTES
:
106 return (PAPI_ATTRIBUTES
);
107 case IPP_CERR_URI_SCHEME
:
108 return (PAPI_URI_SCHEME
);
109 case IPP_CERR_CHARSET
:
110 return (PAPI_CHARSET
);
111 case IPP_CERR_CONFLICT
:
112 return (PAPI_CONFLICT
);
113 case IPP_CERR_COMPRESSION_NOT_SUPPORTED
:
114 return (PAPI_COMPRESSION_NOT_SUPPORTED
);
115 case IPP_CERR_COMPRESSION_ERROR
:
116 return (PAPI_COMPRESSION_ERROR
);
117 case IPP_CERR_DOCUMENT_FORMAT_ERROR
:
118 return (PAPI_DOCUMENT_FORMAT_ERROR
);
119 case IPP_CERR_DOCUMENT_ACCESS_ERROR
:
120 return (PAPI_DOCUMENT_ACCESS_ERROR
);
121 case IPP_CERR_ATTRIBUTES_NOT_SETTABLE
:
122 return (PAPI_ATTRIBUTES_NOT_SETTABLE
);
123 case IPP_CERR_IGNORED_ALL_SUBSCRIPTIONS
:
124 return (PAPI_IGNORED_ALL_SUBSCRIPTIONS
);
125 case IPP_CERR_TOO_MANY_SUBSCRIPTIONS
:
126 return (PAPI_TOO_MANY_SUBSCRIPTIONS
);
127 case IPP_CERR_IGNORED_ALL_NOTIFICATIONS
:
128 return (PAPI_IGNORED_ALL_NOTIFICATIONS
);
129 case IPP_CERR_PRINT_SUPPORT_FILE_NOT_FOUND
:
130 return (PAPI_PRINT_SUPPORT_FILE_NOT_FOUND
);
131 case IPP_SERR_INTERNAL
:
132 return (PAPI_INTERNAL_ERROR
);
133 case IPP_SERR_OPERATION_NOT_SUPPORTED
:
134 return (PAPI_OPERATION_NOT_SUPPORTED
);
135 case IPP_SERR_SERVICE_UNAVAILABLE
:
136 return (PAPI_SERVICE_UNAVAILABLE
);
137 case IPP_SERR_VERSION_NOT_SUPPORTED
:
138 return (PAPI_VERSION_NOT_SUPPORTED
);
139 case IPP_SERR_DEVICE_ERROR
:
140 return (PAPI_DEVICE_ERROR
);
141 case IPP_SERR_TEMPORARY_ERROR
:
142 return (PAPI_TEMPORARY_ERROR
);
143 case IPP_SERR_NOT_ACCEPTING
:
144 return (PAPI_NOT_ACCEPTING
);
146 case IPP_SERR_CANCELLED
:
148 return (PAPI_TEMPORARY_ERROR
);
153 ipp_initialize_request(service_t
*svc
, papi_attribute_t
***request
,
156 papiAttributeListAddInteger(request
, PAPI_ATTR_EXCL
,
158 papiAttributeListAddInteger(request
, PAPI_ATTR_EXCL
,
160 papiAttributeListAddInteger(request
, PAPI_ATTR_EXCL
,
161 "request-id", (short)lrand48());
162 papiAttributeListAddInteger(request
, PAPI_ATTR_EXCL
,
163 "operation-id", operation
);
167 ipp_initialize_operational_attributes(service_t
*svc
, papi_attribute_t
***op
,
168 char *printer
, int job_id
)
170 char *charset
= "utf-8"; /* default to UTF-8 encoding */
171 char *language
= setlocale(LC_ALL
, "");
172 char *user
= "nobody";
173 struct passwd
*pw
= NULL
;
176 * All IPP requests must contain the following:
177 * attributes-charset (UTF-8)
178 * attributes-natural-language (our current locale)
179 * (object identifier) printer-uri/job-id or job-uri
180 * requesting-user-name (process user or none)
182 papiAttributeListAddString(op
, PAPI_ATTR_EXCL
,
183 "attributes-charset", charset
);
185 papiAttributeListAddString(op
, PAPI_ATTR_EXCL
,
186 "attributes-natural-language", language
);
189 ipp_add_printer_uri(svc
, printer
, op
);
191 if ((printer
!= NULL
) && (job_id
>= 0))
192 papiAttributeListAddInteger(op
, PAPI_ATTR_EXCL
,
195 if ((pw
= getpwuid(getuid())) != NULL
)
198 * if our euid is 0 "super user", we will allow the system supplied
199 * user name to be overridden, if the requestor wants to.
201 if (geteuid() == 0) {
202 if (svc
->user
!= NULL
)
205 papiAttributeListAddString(op
, PAPI_ATTR_REPLACE
,
206 "requesting-user-name", user
);
209 #ifndef OPID_CUPS_GET_DEFAULT /* for servers that will enumerate */
210 #define OPID_CUPS_GET_DEFAULT 0x4001
211 #endif /* OPID_CUPS_GET_DEFAULT */
214 _default_destination(service_t
*svc
, char **uri
)
216 papi_status_t result
= PAPI_INTERNAL_ERROR
;
218 papi_attribute_t
**request
= NULL
, **op
= NULL
, **response
= NULL
;
221 if ((svc
== NULL
) || (uri
== NULL
))
222 return (PAPI_BAD_ARGUMENT
);
224 /* we must be connected to find the default destination */
225 if (svc
->connection
== NULL
)
226 return (PAPI_NOT_POSSIBLE
);
228 if ((p
= calloc(1, sizeof (*p
))) == NULL
)
229 return (PAPI_TEMPORARY_ERROR
);
231 ipp_initialize_request(svc
, &request
, OPID_CUPS_GET_DEFAULT
);
232 ipp_initialize_operational_attributes(svc
, &op
, NULL
, -1);
233 papiAttributeListAddString(&op
, PAPI_ATTR_APPEND
,
234 "requested-attributes", "printer-uri-supported");
235 papiAttributeListAddCollection(&request
, PAPI_ATTR_REPLACE
,
236 "operational-attributes-group", op
);
237 papiAttributeListFree(op
);
238 result
= ipp_send_request(svc
, request
, &response
);
239 papiAttributeListFree(request
);
242 papiAttributeListGetCollection(response
, NULL
,
243 "printer-attributes-group", &op
);
248 papiAttributeListGetString(op
, NULL
, "printer-uri", &tmp
);
249 papiAttributeListGetString(op
, NULL
,
250 "printer-uri-supported", &tmp
);
255 papiAttributeListFree(response
);
261 ipp_add_printer_uri(service_t
*svc
, char *name
, papi_attribute_t
***op
)
267 if (strstr(name
, "://") == NULL
) { /* not in URI form */
268 if (strcmp(name
, DEFAULT_DEST
) != 0) {
269 /* not the "default" */
270 snprintf(buf
, sizeof (buf
), "%s/%s", svc
->name
, name
);
273 _default_destination(svc
, &uri
);
276 papiAttributeListAddString(op
, PAPI_ATTR_EXCL
, "printer-uri", uri
);
278 /* save the printer-uri's path to be used by http POST request */
279 if ((uri_from_string(uri
, &tmp
) == 0) && (tmp
!= NULL
)) {
280 if (svc
->post
!= NULL
)
282 svc
->post
= strdup(tmp
->path
);
289 * don't actually write anything, just add to the total size and return the
290 * size of what would be written, so we can figure out how big the request
294 size_calculate(void *fd
, void *buffer
, size_t length
)
296 ssize_t
*size
= (ssize_t
*)fd
;
304 build_chunk(void *fd
, void *buffer
, size_t length
)
308 memcpy(*s1
, buffer
, length
);
315 ipp_request_write(void *fd
, void *buffer
, size_t length
)
317 service_t
*svc
= (service_t
*)fd
;
320 printf("ipp_request_write(0x%8.8x, 0x%8.8x, %d)\n", fd
, buffer
, length
);
321 httpDumpData(stdout
, "ipp_request_write:", buffer
, length
);
323 return (httpWrite(svc
->connection
, buffer
, length
));
327 ipp_request_read(void *fd
, void *buffer
, size_t length
)
329 service_t
*svc
= (service_t
*)fd
;
330 ssize_t rc
, i
= length
;
333 while ((rc
= httpRead(svc
->connection
, p
, i
)) != i
) {
342 printf("ipp_request_read(0x%8.8x, 0x%8.8x, %d) = %d\n",
343 fd
, buffer
, length
, rc
);
344 httpDumpData(stdout
, "ipp_request_read:", buffer
, length
);
351 ipp_send_initial_request_block(service_t
*svc
, papi_attribute_t
**request
,
354 papi_status_t result
= PAPI_OK
;
355 ssize_t chunk_size
= 0;
358 http_status_t status
;
360 /* calculate the request size */
361 (void) ipp_write_message(&size_calculate
, &chunk_size
, request
);
363 /* Fill in the HTTP Header information */
364 httpClearFields(svc
->connection
);
365 if (svc
->transfer_encoding
== TRANSFER_ENCODING_CHUNKED
)
366 httpSetField(svc
->connection
, HTTP_FIELD_TRANSFER_ENCODING
,
369 sprintf(length
, "%lu", (unsigned long)(file_size
+ chunk_size
));
370 httpSetField(svc
->connection
, HTTP_FIELD_CONTENT_LENGTH
,
373 httpSetField(svc
->connection
, HTTP_FIELD_CONTENT_TYPE
,
375 httpSetField(svc
->connection
, HTTP_FIELD_AUTHORIZATION
,
376 svc
->connection
->authstring
);
378 /* flush any state information about this connection */
379 httpFlush(svc
->connection
);
381 /* if we have don't have a POST path, use the service uri path */
382 if ((svc
->post
== NULL
) && (svc
->uri
->path
))
383 svc
->post
= strdup(svc
->uri
->path
);
384 /* send the HTTP POST message for the IPP request */
385 /* if the POST fails, return the error */
386 status
= httpPost(svc
->connection
, svc
->post
);
388 return (http_to_papi_status(status
));
390 if (httpCheck(svc
->connection
) != 0) {
391 status
= httpUpdate(svc
->connection
);
392 if (status
!= HTTP_OK
)
393 return (http_to_papi_status(status
));
396 /* build the request chunk */
397 chunk
= ptr
= calloc(1, chunk_size
);
398 result
= ipp_write_message(&build_chunk
, &ptr
, request
);
400 printf("request: %d (0x%x) bytes\n", chunk_size
, chunk_size
);
401 httpDumpData(stdout
, "request:", chunk
, chunk_size
);
404 /* send the actual IPP request */
405 if (ipp_request_write(svc
, chunk
, chunk_size
) != chunk_size
)
406 result
= PAPI_TEMPORARY_ERROR
;
409 if (httpCheck(svc
->connection
) != 0) {
410 status
= httpUpdate(svc
->connection
);
411 if (status
!= HTTP_OK
)
412 return (http_to_papi_status(status
));
419 setAuthString(service_t
*svc
)
422 char *user
, *passphrase
;
423 char encoded
[BUFSIZ
];
425 if ((svc
== NULL
) || (svc
->connection
== NULL
) || (svc
->name
== NULL
))
428 http
= svc
->connection
;
430 if (svc
->user
== NULL
) {
433 if ((p
= getpwuid(getuid())) != NULL
) {
435 } else if ((user
= getenv("LOGNAME")) == NULL
)
436 user
= getenv("USER");
442 /* if the passphrase is not set, use the Authentication Callback */
443 if (((svc
->password
== NULL
) || (svc
->password
[0] == '\0')) &&
444 (svc
->authCB
!= NULL
))
445 (svc
->authCB
)(svc
, svc
->app_data
);
446 passphrase
= svc
->password
;
448 /* if there is still no passphrase, we have to fail */
449 if ((passphrase
== NULL
) || (passphrase
[0] == '\0'))
452 if (strncmp(http
->fields
[HTTP_FIELD_WWW_AUTHENTICATE
],
456 snprintf(plain
, sizeof (plain
), "%s:%s", user
, passphrase
);
457 httpEncode64(encoded
, plain
);
458 snprintf(http
->authstring
, sizeof (http
->authstring
),
459 "Basic %s", encoded
);
460 } else if (strncmp(http
->fields
[HTTP_FIELD_WWW_AUTHENTICATE
],
462 char realm
[HTTP_MAX_VALUE
];
463 char nonce
[HTTP_MAX_VALUE
];
467 char *uri
= svc
->post
;
469 httpGetSubField(http
, HTTP_FIELD_WWW_AUTHENTICATE
,
471 httpGetSubField(http
, HTTP_FIELD_WWW_AUTHENTICATE
,
474 snprintf(line
, sizeof (line
), "%s:%s:%s", user
, realm
,
476 md5_calc(urp
, line
, strlen(line
));
478 snprintf(line
, sizeof (line
), "POST:%s", uri
);
479 md5_calc(mr
, line
, strlen(line
));
481 snprintf(line
, sizeof (line
), "%s:%s:%s", urp
, mr
, nonce
);
482 md5_calc(encoded
, line
, strlen(line
));
484 snprintf(http
->authstring
, sizeof (http
->authstring
),
485 "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", "
486 "uri=\"%s\", response=\"%s\"", user
, realm
, nonce
, uri
,
494 ipp_status_info(service_t
*svc
, papi_attribute_t
**response
)
496 papi_attribute_t
**operational
= NULL
;
499 papiAttributeListGetCollection(response
, NULL
,
500 "operational-attributes-group", &operational
);
501 if (operational
!= NULL
) {
502 char *message
= NULL
;
504 papiAttributeListGetString(response
, NULL
,
505 "status-message", &message
);
506 papiAttributeListAddString(&svc
->attributes
, PAPI_ATTR_REPLACE
,
507 "detailed-status-message", message
);
509 papiAttributeListGetInteger(response
, NULL
, "status-code", &status
);
511 return (ipp_to_papi_status(status
));
515 ipp_send_request_with_file(service_t
*svc
, papi_attribute_t
**request
,
516 papi_attribute_t
***response
, char *file
)
518 papi_status_t result
= PAPI_OK
;
524 fprintf(stderr
, "\nIPP-REQUEST: (%s)", (file
? file
: ""));
525 papiAttributeListPrint(stderr
, request
, " ");
531 * if we are sending a file, open it and include it's size in the
535 if ((fd
= open(file
, O_RDONLY
)) < 0) {
536 detailed_error(svc
, "%s: %s", file
, strerror(errno
));
537 return (PAPI_DOCUMENT_ACCESS_ERROR
);
538 } else if (strcmp("standard input", file
) != 0) {
539 if (stat(file
, &statbuf
) < 0) {
541 gettext("Cannot access file: %s: %s"),
542 file
, strerror(errno
));
543 return (PAPI_DOCUMENT_ACCESS_ERROR
);
545 if (statbuf
.st_size
== 0) {
547 "Zero byte (empty) file: %s", file
);
548 return (PAPI_BAD_ARGUMENT
);
550 } else if (svc
->transfer_encoding
!=
551 TRANSFER_ENCODING_CHUNKED
) {
554 if (fstat(fd
, &st
) >= 0)
560 while (*response
== NULL
) {
561 http_status_t status
= HTTP_CONTINUE
;
563 result
= ipp_send_initial_request_block(svc
, request
, size
);
565 if (result
== PAPI_OK
) {
567 /* send the file contents if we have it */
571 lseek(fd
, 0L, SEEK_SET
);
572 while ((rc
= read(fd
, buf
, sizeof (buf
))) > 0) {
573 if (ipp_request_write(svc
, buf
, rc
)
580 (void) ipp_request_write(svc
, "", 0);
583 /* update our connection info */
584 while (status
== HTTP_CONTINUE
)
585 status
= httpUpdate(svc
->connection
);
587 if (status
== HTTP_UNAUTHORIZED
) {
588 httpFlush(svc
->connection
);
589 if ((svc
->connection
->authstring
[0] == '\0') &&
590 (setAuthString(svc
) == 0)) {
591 httpReconnect(svc
->connection
);
594 } else if (status
== HTTP_UPGRADE_REQUIRED
) {
596 * If the transport was built with TLS support, we can
599 httpFlush(svc
->connection
);
600 httpReconnect(svc
->connection
);
601 httpEncryption(svc
->connection
, HTTP_ENCRYPT_REQUIRED
);
605 if (status
!= HTTP_OK
)
606 return (http_to_papi_status(status
));
608 /* read the IPP response */
609 result
= ipp_read_message(&ipp_request_read
, svc
, response
,
612 if (result
== PAPI_OK
)
613 result
= ipp_status_info(svc
, *response
);
615 fprintf(stderr
, "\nIPP-RESPONSE: (%s) (%s)", (file
? file
: ""),
616 papiStatusString(result
));
617 papiAttributeListPrint(stderr
, *response
, " ");
627 ipp_send_request(service_t
*svc
, papi_attribute_t
**request
,
628 papi_attribute_t
***response
)
630 return (ipp_send_request_with_file(svc
, request
, response
, NULL
));