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