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