1 /*
2 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 #pragma ident "$Id: ipp-support.c,v 1.8 2006/02/27 06:10:10 njacobs Exp $"
7
8 #include <papi_impl.h>
9 #include <stdlib.h>
10 #include <pwd.h>
11 #include <locale.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <sys/stat.h>
15
16 #include <config-site.h>
17
18 papi_status_t
http_to_papi_status(http_status_t status)19 http_to_papi_status(http_status_t status)
20 {
21 switch (status) {
22 case HTTP_OK:
23 return (PAPI_OK);
24 case HTTP_BAD_REQUEST:
25 return (PAPI_BAD_REQUEST);
26 case HTTP_UNAUTHORIZED:
27 case HTTP_FORBIDDEN:
28 return (PAPI_NOT_AUTHORIZED);
29 case HTTP_NOT_FOUND:
30 return (PAPI_NOT_FOUND);
31 case HTTP_GONE:
32 return (PAPI_GONE);
33 case HTTP_SERVICE_UNAVAILABLE:
34 return (PAPI_SERVICE_UNAVAILABLE);
35 default:
36 return ((papi_status_t)status);
37 }
38 }
39
40 papi_status_t
ipp_to_papi_status(uint16_t status)41 ipp_to_papi_status(uint16_t status)
42 {
43 switch (status) {
44 case IPP_OK:
45 return (PAPI_OK);
46 case IPP_OK_IGNORED_ATTRIBUTES:
47 return (PAPI_OK);
48 case IPP_OK_CONFLICTING_ATTRIBUTES:
49 return (PAPI_OK);
50 case IPP_OK_IGNORED_SUBSCRIPTIONS:
51 return (PAPI_OK_IGNORED_SUBSCRIPTIONS);
52 case IPP_OK_IGNORED_NOTIFICATIONS:
53 return (PAPI_OK_IGNORED_NOTIFICATIONS);
54 case IPP_CERR_BAD_REQUEST:
55 return (PAPI_BAD_REQUEST);
56 case IPP_CERR_FORBIDDEN:
57 return (PAPI_FORBIDDEN);
58 case IPP_CERR_NOT_AUTHENTICATED:
59 return (PAPI_NOT_AUTHENTICATED);
60 case IPP_CERR_NOT_AUTHORIZED:
61 return (PAPI_NOT_AUTHORIZED);
62 case IPP_CERR_NOT_POSSIBLE:
63 return (PAPI_NOT_POSSIBLE);
64 case IPP_CERR_TIMEOUT:
65 return (PAPI_TIMEOUT);
66 case IPP_CERR_NOT_FOUND:
67 return (PAPI_NOT_FOUND);
68 case IPP_CERR_GONE:
69 return (PAPI_GONE);
70 case IPP_CERR_REQUEST_ENTITY:
71 return (PAPI_REQUEST_ENTITY);
72 case IPP_CERR_REQUEST_VALUE:
73 return (PAPI_REQUEST_VALUE);
74 case IPP_CERR_DOCUMENT_FORMAT:
75 return (PAPI_DOCUMENT_FORMAT);
76 case IPP_CERR_ATTRIBUTES:
77 return (PAPI_ATTRIBUTES);
78 case IPP_CERR_URI_SCHEME:
79 return (PAPI_URI_SCHEME);
80 case IPP_CERR_CHARSET:
81 return (PAPI_CHARSET);
82 case IPP_CERR_CONFLICT:
83 return (PAPI_CONFLICT);
84 case IPP_CERR_COMPRESSION_NOT_SUPPORTED:
85 return (PAPI_COMPRESSION_NOT_SUPPORTED);
86 case IPP_CERR_COMPRESSION_ERROR:
87 return (PAPI_COMPRESSION_ERROR);
88 case IPP_CERR_DOCUMENT_FORMAT_ERROR:
89 return (PAPI_DOCUMENT_FORMAT_ERROR);
90 case IPP_CERR_DOCUMENT_ACCESS_ERROR:
91 return (PAPI_DOCUMENT_ACCESS_ERROR);
92 case IPP_CERR_ATTRIBUTES_NOT_SETTABLE:
93 return (PAPI_ATTRIBUTES_NOT_SETTABLE);
94 case IPP_CERR_IGNORED_ALL_SUBSCRIPTIONS:
95 return (PAPI_IGNORED_ALL_SUBSCRIPTIONS);
96 case IPP_CERR_TOO_MANY_SUBSCRIPTIONS:
97 return (PAPI_TOO_MANY_SUBSCRIPTIONS);
98 case IPP_CERR_IGNORED_ALL_NOTIFICATIONS:
99 return (PAPI_IGNORED_ALL_NOTIFICATIONS);
100 case IPP_CERR_PRINT_SUPPORT_FILE_NOT_FOUND:
101 return (PAPI_PRINT_SUPPORT_FILE_NOT_FOUND);
102 case IPP_SERR_INTERNAL:
103 return (PAPI_INTERNAL_ERROR);
104 case IPP_SERR_OPERATION_NOT_SUPPORTED:
105 return (PAPI_OPERATION_NOT_SUPPORTED);
106 case IPP_SERR_SERVICE_UNAVAILABLE:
107 return (PAPI_SERVICE_UNAVAILABLE);
108 case IPP_SERR_VERSION_NOT_SUPPORTED:
109 return (PAPI_VERSION_NOT_SUPPORTED);
110 case IPP_SERR_DEVICE_ERROR:
111 return (PAPI_DEVICE_ERROR);
112 case IPP_SERR_TEMPORARY_ERROR:
113 return (PAPI_TEMPORARY_ERROR);
114 case IPP_SERR_NOT_ACCEPTING:
115 return (PAPI_NOT_ACCEPTING);
116 case IPP_SERR_BUSY:
117 case IPP_SERR_CANCELLED:
118 default:
119 return (PAPI_TEMPORARY_ERROR);
120 }
121 }
122
123 void
ipp_initialize_request(service_t * svc,papi_attribute_t *** request,uint16_t operation)124 ipp_initialize_request(service_t *svc, papi_attribute_t ***request,
125 uint16_t operation)
126 {
127 papiAttributeListAddInteger(request, PAPI_ATTR_EXCL,
128 "version-major", 1);
129 papiAttributeListAddInteger(request, PAPI_ATTR_EXCL,
130 "version-minor", 1);
131 papiAttributeListAddInteger(request, PAPI_ATTR_EXCL,
132 "request-id", (short)lrand48());
133 papiAttributeListAddInteger(request, PAPI_ATTR_EXCL,
134 "operation-id", operation);
135 }
136
137 void
ipp_initialize_operational_attributes(service_t * svc,papi_attribute_t *** op,papi_attribute_t ** attributes)138 ipp_initialize_operational_attributes(service_t *svc, papi_attribute_t ***op,
139 papi_attribute_t **attributes)
140 {
141 char *charset = "utf-8"; /* default to UTF-8 encoding */
142 char *language = setlocale(LC_ALL, "");
143 char *user = "nobody";
144 struct passwd *pw = NULL;
145
146 /*
147 * All IPP requests must contain the following:
148 * attributes-charset (UTF-8)
149 * attributes-natural-language (our current locale)
150 * requesting-user-name (process user)
151 */
152 papiAttributeListGetString(attributes, NULL,
153 "attributes-charset", &charset);
154 papiAttributeListAddString(op, PAPI_ATTR_EXCL,
155 "attributes-charset", charset);
156
157 papiAttributeListGetString(attributes, NULL,
158 "attributes-natural-language", &language);
159 papiAttributeListAddString(op, PAPI_ATTR_EXCL,
160 "attributes-natural-language", language);
161
162 if ((pw = getpwuid(getuid())) != NULL)
163 user = pw->pw_name;
164 /*
165 * if our euid is 0 "super user", we will allow the system supplied
166 * user name to be overridden, if the requestor wants to.
167 */
168 if (geteuid() == 0) {
169 if (svc->user != NULL)
170 user = svc->user;
171 papiAttributeListGetString(attributes, NULL,
172 "requesting-user-name", &user);
173 }
174 papiAttributeListAddString(op, PAPI_ATTR_REPLACE,
175 "requesting-user-name", user);
176 }
177
178 #ifndef OPID_CUPS_GET_DEFAULT /* for servers that will enumerate */
179 #define OPID_CUPS_GET_DEFAULT 0x4001
180 #endif /* OPID_CUPS_GET_DEFAULT */
181
182 static papi_status_t
_default_destination(service_t * svc,char ** uri)183 _default_destination(service_t *svc, char **uri)
184 {
185 papi_status_t result = PAPI_INTERNAL_ERROR;
186 printer_t *p = NULL;
187 papi_attribute_t **request = NULL, **op = NULL, **response = NULL;
188 char *tmp = NULL;
189
190 if ((svc == NULL) || (uri == NULL))
191 return (PAPI_BAD_ARGUMENT);
192
193 /* we must be connected to find the default destination */
194 if (svc->connection == NULL)
195 return (PAPI_NOT_POSSIBLE);
196
197 if ((p = calloc(1, sizeof (*p))) == NULL)
198 return (PAPI_TEMPORARY_ERROR);
199
200 ipp_initialize_request(svc, &request, OPID_CUPS_GET_DEFAULT);
201 ipp_initialize_operational_attributes(svc, &op, NULL);
202 papiAttributeListAddString(&op, PAPI_ATTR_APPEND,
203 "requested-attributes", "printer-uri-supported");
204 papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
205 "operational-attributes-group", op);
206 papiAttributeListFree(op);
207 result = ipp_send_request(svc, request, &response);
208 papiAttributeListFree(request);
209
210 op = NULL;
211 papiAttributeListGetCollection(response, NULL,
212 "printer-attributes-group", &op);
213
214 if (uri != NULL) {
215 char *tmp = NULL;
216
217 papiAttributeListGetString(op, NULL, "printer-uri", &tmp);
218 papiAttributeListGetString(op, NULL,
219 "printer-uri-supported", &tmp);
220 if (tmp != NULL)
221 *uri = strdup(tmp);
222 }
223
224 papiAttributeListFree(response);
225
226 return (result);
227 }
228
229 void
ipp_add_printer_uri(service_t * svc,char * name,papi_attribute_t *** op)230 ipp_add_printer_uri(service_t *svc, char *name, papi_attribute_t ***op)
231 {
232 char *uri = name;
233 char buf[BUFSIZ];
234 uri_t *tmp = NULL;
235
236 if (strstr(name, "://") == NULL) { /* not in URI form */
237 if (strcmp(name, DEFAULT_DEST) != 0) {
238 /* not the "default" */
239 snprintf(buf, sizeof (buf), "%s/%s", svc->name, name);
240 uri = buf;
241 } else
242 _default_destination(svc, &uri);
243 }
244
245 papiAttributeListAddString(op, PAPI_ATTR_EXCL, "printer-uri", uri);
246
247 /* save the printer-uri's path to be used by http POST request */
248 if ((uri_from_string(uri, &tmp) == 0) && (tmp != NULL)) {
249 if (svc->post != NULL)
250 free(svc->post);
251 svc->post = strdup(tmp->path);
252 uri_free(tmp);
253 }
254 }
255
256
257 /*
258 * don't actually write anything, just add to the total size and return the
259 * size of what would be written, so we can figure out how big the request
260 * is going to be.
261 */
262 static ssize_t
size_calculate(void * fd,void * buffer,size_t length)263 size_calculate(void *fd, void *buffer, size_t length)
264 {
265 ssize_t *size = (ssize_t *)fd;
266
267 *size += length;
268 return (length);
269 }
270
271
272 static ssize_t
build_chunk(void * fd,void * buffer,size_t length)273 build_chunk(void *fd, void *buffer, size_t length)
274 {
275 char **s1 = fd;
276
277 memcpy(*s1, buffer, length);
278 *s1 = *s1 + length;
279
280 return (length);
281 }
282
283 ssize_t
ipp_request_write(void * fd,void * buffer,size_t length)284 ipp_request_write(void *fd, void *buffer, size_t length)
285 {
286 service_t *svc = (service_t *)fd;
287
288 #ifdef DEBUG
289 printf("ipp_request_write(0x%8.8x, 0x%8.8x, %d)\n", fd, buffer, length);
290 httpDumpData(stdout, "ipp_request_write:", buffer, length);
291 #endif
292 return (httpWrite(svc->connection, buffer, length));
293 }
294
295 ssize_t
ipp_request_read(void * fd,void * buffer,size_t length)296 ipp_request_read(void *fd, void *buffer, size_t length)
297 {
298 service_t *svc = (service_t *)fd;
299 ssize_t rc, i = length;
300 void *p = buffer;
301
302 while ((rc = httpRead(svc->connection, p, i)) != i) {
303 if (rc == 0)
304 return (rc);
305 if (rc < 0)
306 return (rc);
307 i -= rc;
308 p += rc;
309 }
310 #ifdef DEBUG
311 printf("ipp_request_read(0x%8.8x, 0x%8.8x, %d) = %d\n",
312 fd, buffer, length, rc);
313 httpDumpData(stdout, "ipp_request_read:", buffer, length);
314 #endif
315
316 return (length);
317 }
318
319 papi_status_t
ipp_send_initial_request_block(service_t * svc,papi_attribute_t ** request,ssize_t file_size)320 ipp_send_initial_request_block(service_t *svc, papi_attribute_t **request,
321 ssize_t file_size)
322 {
323 papi_status_t result = PAPI_OK;
324 ssize_t chunk_size = 0;
325 char length[32];
326 void *chunk, *ptr;
327 http_status_t status;
328
329 /* calculate the request size */
330 (void) ipp_write_message(&size_calculate, &chunk_size, request);
331
332 /* Fill in the HTTP Header information */
333 httpClearFields(svc->connection);
334 if (svc->transfer_encoding == TRANSFER_ENCODING_CHUNKED)
335 httpSetField(svc->connection, HTTP_FIELD_TRANSFER_ENCODING,
336 "chunked");
337 else {
338 sprintf(length, "%lu", (unsigned long)(file_size + chunk_size));
339 httpSetField(svc->connection, HTTP_FIELD_CONTENT_LENGTH,
340 length);
341 }
342 httpSetField(svc->connection, HTTP_FIELD_CONTENT_TYPE,
343 "application/ipp");
344 httpSetField(svc->connection, HTTP_FIELD_AUTHORIZATION,
345 svc->connection->authstring);
346
347 /* flush any state information about this connection */
348 httpFlush(svc->connection);
349
350 /* if we have don't have a POST path, use the service uri path */
351 if (svc->post == NULL)
352 svc->post = strdup(svc->uri->path);
353 /* send the HTTP POST message for the IPP request */
354 /* if the POST fails, return the error */
355 status = httpPost(svc->connection, svc->post);
356 if (status != 0)
357 return (http_to_papi_status(status));
358
359 if (httpCheck(svc->connection) != 0) {
360 status = httpUpdate(svc->connection);
361 if (status != HTTP_OK)
362 return (http_to_papi_status(status));
363 }
364
365 /* build the request chunk */
366 chunk = ptr = calloc(1, chunk_size);
367 result = ipp_write_message(&build_chunk, &ptr, request);
368 #ifdef DEBUG
369 printf("request: %d (0x%x) bytes\n", chunk_size, chunk_size);
370 httpDumpData(stdout, "request:", chunk, chunk_size);
371 #endif
372
373 /* send the actual IPP request */
374 if (ipp_request_write(svc, chunk, chunk_size) != chunk_size)
375 result = PAPI_TEMPORARY_ERROR;
376 free(chunk);
377
378 if (httpCheck(svc->connection) != 0) {
379 status = httpUpdate(svc->connection);
380 if (status != HTTP_OK)
381 return (http_to_papi_status(status));
382 }
383
384 return (result);
385 }
386
387 static int
setAuthString(service_t * svc)388 setAuthString(service_t *svc)
389 {
390 http_t *http;
391 char *user, *passphrase;
392 char encoded[BUFSIZ];
393
394 if ((svc == NULL) || (svc->connection == NULL) || (svc->name == NULL))
395 return (-1);
396
397 http = svc->connection;
398
399 if (svc->user == NULL) {
400 struct passwd *p;
401
402 if ((p = getpwuid(getuid())) != NULL) {
403 user = p->pw_name;
404 } else if ((user = getenv("LOGNAME")) == NULL)
405 user = getenv("USER");
406 if (user == NULL)
407 user = "nobody";
408 } else
409 user = svc->user;
410
411 /* if the passphrase is not set, use the Authentication Callback */
412 if (((svc->password == NULL) || (svc->password[0] == '\0')) &&
413 (svc->authCB != NULL))
414 (svc->authCB)(svc);
415 passphrase = svc->password;
416
417 /* if there is still no passphrase, we have to fail */
418 if ((passphrase == NULL) || (passphrase[0] == '\0'))
419 return (-1);
420
421 if (strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE],
422 "Basic", 5) == 0) {
423 char plain[BUFSIZ];
424
425 snprintf(plain, sizeof (plain), "%s:%s", user, passphrase);
426 httpEncode64(encoded, plain);
427 snprintf(http->authstring, sizeof (http->authstring),
428 "Basic %s", encoded);
429 } else if (strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE],
430 "Digest", 6) == 0) {
431 char realm[HTTP_MAX_VALUE];
432 char nonce[HTTP_MAX_VALUE];
433 char *uri = svc->post;
434
435 httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE,
436 "realm", realm);
437 httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE,
438 "nonce", nonce);
439 httpMD5(user, realm, passphrase, encoded);
440 httpMD5Final(nonce, "POST", uri, encoded);
441
442 snprintf(http->authstring, sizeof (http->authstring),
443 "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", "
444 "uri=\"%s\", response=\"%s\"", user, realm, nonce, uri,
445 encoded);
446 }
447
448 return (0);
449 }
450
451 papi_status_t
ipp_status_info(service_t * svc,papi_attribute_t ** response)452 ipp_status_info(service_t *svc, papi_attribute_t **response)
453 {
454 papi_attribute_t **operational = NULL;
455 int32_t status = 0;
456
457 papiAttributeListGetCollection(response, NULL,
458 "operational-attributes-group", &operational);
459 if (operational != NULL) {
460 char *message = NULL;
461
462 papiAttributeListGetString(response, NULL,
463 "status-message", &message);
464 papiAttributeListAddString(&svc->attributes, PAPI_ATTR_REPLACE,
465 "detailed-status-message", message);
466 }
467 papiAttributeListGetInteger(response, NULL, "status-code", &status);
468
469 return (ipp_to_papi_status(status));
470 }
471
472 papi_status_t
ipp_send_request_with_file(service_t * svc,papi_attribute_t ** request,papi_attribute_t *** response,char * file)473 ipp_send_request_with_file(service_t *svc, papi_attribute_t **request,
474 papi_attribute_t ***response, char *file)
475 {
476 papi_status_t result = PAPI_OK;
477 ssize_t size = 0;
478 int fd;
479
480 #ifdef DEBUG
481 fprintf(stderr, "\nIPP-REQUEST: (%s)", (file ? file : ""));
482 papiAttributeListPrint(stderr, request, " ");
483 putc('\n', stderr);
484 fflush(stderr);
485 #endif
486
487 /*
488 * if we are sending a file, open it and include it's size in the
489 * message size.
490 */
491 if (file != NULL) {
492 if ((fd = open(file, O_RDONLY)) < 0) {
493 detailed_error(svc, "%s: %s", file, strerror(errno));
494 return (PAPI_DOCUMENT_ACCESS_ERROR);
495 } else if (svc->transfer_encoding !=
496 TRANSFER_ENCODING_CHUNKED) {
497 struct stat st;
498
499 if (fstat(fd, &st) >= 0)
500 size = st.st_size;
501 }
502 }
503
504 *response = NULL;
505 while (*response == NULL) {
506 http_status_t status = HTTP_CONTINUE;
507
508 result = ipp_send_initial_request_block(svc, request, size);
509
510 if (result == PAPI_OK) {
511 if (file != NULL) {
512 /* send the file contents if we have it */
513 int rc;
514 char buf[BUFSIZ];
515
516 lseek(fd, 0L, SEEK_SET);
517 while ((rc = read(fd, buf, sizeof (buf))) > 0) {
518 if (ipp_request_write(svc, buf, rc)
519 < rc) {
520 break;
521 }
522 }
523 }
524
525 (void) ipp_request_write(svc, "", 0);
526 }
527
528 /* update our connection info */
529 while (status == HTTP_CONTINUE)
530 status = httpUpdate(svc->connection);
531
532 if (status == HTTP_UNAUTHORIZED) {
533 httpFlush(svc->connection);
534 if ((svc->connection->authstring[0] == '\0') &&
535 (setAuthString(svc) == 0)) {
536 httpReconnect(svc->connection);
537 continue;
538 }
539 } else if (status == HTTP_UPGRADE_REQUIRED) {
540 /*
541 * If the transport was built with TLS support, we can
542 * try to use it.
543 */
544 httpFlush(svc->connection);
545 httpReconnect(svc->connection);
546 httpEncryption(svc->connection, HTTP_ENCRYPT_REQUIRED);
547 continue;
548 }
549
550 if (status != HTTP_OK)
551 return (http_to_papi_status(status));
552
553 /* read the IPP response */
554 result = ipp_read_message(&ipp_request_read, svc, response,
555 IPP_TYPE_RESPONSE);
556
557 if (result == PAPI_OK)
558 result = ipp_status_info(svc, *response);
559 #ifdef DEBUG
560 fprintf(stderr, "\nIPP-RESPONSE: (%s) (%s)", (file ? file : ""),
561 papiStatusString(result));
562 papiAttributeListPrint(stderr, *response, " ");
563 putc('\n', stderr);
564 fflush(stderr);
565 #endif
566 }
567
568 return (result);
569 }
570
571 papi_status_t
ipp_send_request(service_t * svc,papi_attribute_t ** request,papi_attribute_t *** response)572 ipp_send_request(service_t *svc, papi_attribute_t **request,
573 papi_attribute_t ***response)
574 {
575 return (ipp_send_request_with_file(svc, request, response, NULL));
576 }
577