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