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 2009 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 31 #include <papi_impl.h> 32 #include <stdlib.h> 33 #include <pwd.h> 34 #include <locale.h> 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <sys/stat.h> 38 #include <md5.h> 39 40 #include <config-site.h> 41 42 #include <ipp.h> 43 44 static void ipp_add_printer_uri(service_t *svc, char *name, 45 papi_attribute_t ***op); 46 47 papi_status_t 48 http_to_papi_status(http_status_t status) 49 { 50 switch (status) { 51 case HTTP_OK: 52 return (PAPI_OK); 53 case HTTP_BAD_REQUEST: 54 return (PAPI_BAD_REQUEST); 55 case HTTP_UNAUTHORIZED: 56 case HTTP_FORBIDDEN: 57 return (PAPI_NOT_AUTHORIZED); 58 case HTTP_NOT_FOUND: 59 return (PAPI_NOT_FOUND); 60 case HTTP_GONE: 61 return (PAPI_GONE); 62 case HTTP_SERVICE_UNAVAILABLE: 63 return (PAPI_SERVICE_UNAVAILABLE); 64 default: 65 return ((papi_status_t)status); 66 } 67 } 68 69 papi_status_t 70 ipp_to_papi_status(uint16_t status) 71 { 72 switch (status) { 73 case IPP_OK: 74 return (PAPI_OK); 75 case IPP_OK_IGNORED_ATTRIBUTES: 76 return (PAPI_OK); 77 case IPP_OK_CONFLICTING_ATTRIBUTES: 78 return (PAPI_OK); 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); 97 case IPP_CERR_GONE: 98 return (PAPI_GONE); 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); 145 case IPP_SERR_BUSY: 146 case IPP_SERR_CANCELLED: 147 default: 148 return (PAPI_TEMPORARY_ERROR); 149 } 150 } 151 152 void 153 ipp_initialize_request(service_t *svc, papi_attribute_t ***request, 154 uint16_t operation) 155 { 156 papiAttributeListAddInteger(request, PAPI_ATTR_EXCL, 157 "version-major", 1); 158 papiAttributeListAddInteger(request, PAPI_ATTR_EXCL, 159 "version-minor", 1); 160 papiAttributeListAddInteger(request, PAPI_ATTR_EXCL, 161 "request-id", (short)lrand48()); 162 papiAttributeListAddInteger(request, PAPI_ATTR_EXCL, 163 "operation-id", operation); 164 } 165 166 void 167 ipp_initialize_operational_attributes(service_t *svc, papi_attribute_t ***op, 168 char *printer, int job_id) 169 { 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; 174 175 /* 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) 181 */ 182 papiAttributeListAddString(op, PAPI_ATTR_EXCL, 183 "attributes-charset", charset); 184 185 papiAttributeListAddString(op, PAPI_ATTR_EXCL, 186 "attributes-natural-language", language); 187 188 if (printer != NULL) 189 ipp_add_printer_uri(svc, printer, op); 190 191 if ((printer != NULL) && (job_id >= 0)) 192 papiAttributeListAddInteger(op, PAPI_ATTR_EXCL, 193 "job-id", job_id); 194 195 if ((pw = getpwuid(getuid())) != NULL) 196 user = pw->pw_name; 197 /* 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. 200 */ 201 if (geteuid() == 0) { 202 if (svc->user != NULL) 203 user = svc->user; 204 } 205 papiAttributeListAddString(op, PAPI_ATTR_REPLACE, 206 "requesting-user-name", user); 207 } 208 209 #ifndef OPID_CUPS_GET_DEFAULT /* for servers that will enumerate */ 210 #define OPID_CUPS_GET_DEFAULT 0x4001 211 #endif /* OPID_CUPS_GET_DEFAULT */ 212 213 static papi_status_t 214 _default_destination(service_t *svc, char **uri) 215 { 216 papi_status_t result = PAPI_INTERNAL_ERROR; 217 printer_t *p = NULL; 218 papi_attribute_t **request = NULL, **op = NULL, **response = NULL; 219 char *tmp = NULL; 220 221 if ((svc == NULL) || (uri == NULL)) 222 return (PAPI_BAD_ARGUMENT); 223 224 /* we must be connected to find the default destination */ 225 if (svc->connection == NULL) 226 return (PAPI_NOT_POSSIBLE); 227 228 if ((p = calloc(1, sizeof (*p))) == NULL) 229 return (PAPI_TEMPORARY_ERROR); 230 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); 240 241 op = NULL; 242 papiAttributeListGetCollection(response, NULL, 243 "printer-attributes-group", &op); 244 245 if (uri != NULL) { 246 char *tmp = NULL; 247 248 papiAttributeListGetString(op, NULL, "printer-uri", &tmp); 249 papiAttributeListGetString(op, NULL, 250 "printer-uri-supported", &tmp); 251 if (tmp != NULL) 252 *uri = strdup(tmp); 253 } 254 255 papiAttributeListFree(response); 256 257 return (result); 258 } 259 260 static void 261 ipp_add_printer_uri(service_t *svc, char *name, papi_attribute_t ***op) 262 { 263 char *uri = name; 264 char buf[BUFSIZ]; 265 uri_t *tmp = NULL; 266 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); 271 uri = buf; 272 } else 273 _default_destination(svc, &uri); 274 } 275 276 papiAttributeListAddString(op, PAPI_ATTR_EXCL, "printer-uri", uri); 277 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) 281 free(svc->post); 282 svc->post = strdup(tmp->path); 283 uri_free(tmp); 284 } 285 } 286 287 288 /* 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 291 * is going to be. 292 */ 293 static ssize_t 294 size_calculate(void *fd, void *buffer, size_t length) 295 { 296 ssize_t *size = (ssize_t *)fd; 297 298 *size += length; 299 return (length); 300 } 301 302 303 static ssize_t 304 build_chunk(void *fd, void *buffer, size_t length) 305 { 306 char **s1 = fd; 307 308 memcpy(*s1, buffer, length); 309 *s1 = *s1 + length; 310 311 return (length); 312 } 313 314 ssize_t 315 ipp_request_write(void *fd, void *buffer, size_t length) 316 { 317 service_t *svc = (service_t *)fd; 318 319 #ifdef DEBUG 320 printf("ipp_request_write(0x%8.8x, 0x%8.8x, %d)\n", fd, buffer, length); 321 httpDumpData(stdout, "ipp_request_write:", buffer, length); 322 #endif 323 return (httpWrite(svc->connection, buffer, length)); 324 } 325 326 ssize_t 327 ipp_request_read(void *fd, void *buffer, size_t length) 328 { 329 service_t *svc = (service_t *)fd; 330 ssize_t rc, i = length; 331 char *p = buffer; 332 333 while ((rc = httpRead(svc->connection, p, i)) != i) { 334 if (rc == 0) 335 return (rc); 336 if (rc < 0) 337 return (rc); 338 i -= rc; 339 p += rc; 340 } 341 #ifdef DEBUG 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); 345 #endif 346 347 return (length); 348 } 349 350 papi_status_t 351 ipp_send_initial_request_block(service_t *svc, papi_attribute_t **request, 352 ssize_t file_size) 353 { 354 papi_status_t result = PAPI_OK; 355 ssize_t chunk_size = 0; 356 char length[32]; 357 void *chunk, *ptr; 358 http_status_t status; 359 360 /* calculate the request size */ 361 (void) ipp_write_message(&size_calculate, &chunk_size, request); 362 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, 367 "chunked"); 368 else { 369 sprintf(length, "%lu", (unsigned long)(file_size + chunk_size)); 370 httpSetField(svc->connection, HTTP_FIELD_CONTENT_LENGTH, 371 length); 372 } 373 httpSetField(svc->connection, HTTP_FIELD_CONTENT_TYPE, 374 "application/ipp"); 375 httpSetField(svc->connection, HTTP_FIELD_AUTHORIZATION, 376 svc->connection->authstring); 377 378 /* flush any state information about this connection */ 379 httpFlush(svc->connection); 380 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); 387 if (status != 0) 388 return (http_to_papi_status(status)); 389 390 if (httpCheck(svc->connection) != 0) { 391 status = httpUpdate(svc->connection); 392 if (status != HTTP_OK) 393 return (http_to_papi_status(status)); 394 } 395 396 /* build the request chunk */ 397 chunk = ptr = calloc(1, chunk_size); 398 result = ipp_write_message(&build_chunk, &ptr, request); 399 #ifdef DEBUG 400 printf("request: %d (0x%x) bytes\n", chunk_size, chunk_size); 401 httpDumpData(stdout, "request:", chunk, chunk_size); 402 #endif 403 404 /* send the actual IPP request */ 405 if (ipp_request_write(svc, chunk, chunk_size) != chunk_size) 406 result = PAPI_TEMPORARY_ERROR; 407 free(chunk); 408 409 if (httpCheck(svc->connection) != 0) { 410 status = httpUpdate(svc->connection); 411 if (status != HTTP_OK) 412 return (http_to_papi_status(status)); 413 } 414 415 return (result); 416 } 417 418 static int 419 setAuthString(service_t *svc) 420 { 421 http_t *http; 422 char *user, *passphrase; 423 char encoded[BUFSIZ]; 424 425 if ((svc == NULL) || (svc->connection == NULL) || (svc->name == NULL)) 426 return (-1); 427 428 http = svc->connection; 429 430 if (svc->user == NULL) { 431 struct passwd *p; 432 433 if ((p = getpwuid(getuid())) != NULL) { 434 user = p->pw_name; 435 } else if ((user = getenv("LOGNAME")) == NULL) 436 user = getenv("USER"); 437 if (user == NULL) 438 user = "nobody"; 439 } else 440 user = svc->user; 441 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; 447 448 /* if there is still no passphrase, we have to fail */ 449 if ((passphrase == NULL) || (passphrase[0] == '\0')) 450 return (-1); 451 452 if (strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], 453 "Basic", 5) == 0) { 454 char plain[BUFSIZ]; 455 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], 461 "Digest", 6) == 0) { 462 char realm[HTTP_MAX_VALUE]; 463 char nonce[HTTP_MAX_VALUE]; 464 char line [BUFSIZ]; 465 char urp[128]; 466 char mr[128]; 467 char *uri = svc->post; 468 469 httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, 470 "realm", realm); 471 httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, 472 "nonce", nonce); 473 474 snprintf(line, sizeof (line), "%s:%s:%s", user, realm, 475 passphrase); 476 md5_calc(urp, line, strlen(line)); 477 478 snprintf(line, sizeof (line), "POST:%s", uri); 479 md5_calc(mr, line, strlen(line)); 480 481 snprintf(line, sizeof (line), "%s:%s:%s", urp, mr, nonce); 482 md5_calc(encoded, line, strlen(line)); 483 484 snprintf(http->authstring, sizeof (http->authstring), 485 "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", " 486 "uri=\"%s\", response=\"%s\"", user, realm, nonce, uri, 487 encoded); 488 } 489 490 return (0); 491 } 492 493 papi_status_t 494 ipp_status_info(service_t *svc, papi_attribute_t **response) 495 { 496 papi_attribute_t **operational = NULL; 497 int32_t status = 0; 498 499 papiAttributeListGetCollection(response, NULL, 500 "operational-attributes-group", &operational); 501 if (operational != NULL) { 502 char *message = NULL; 503 504 papiAttributeListGetString(response, NULL, 505 "status-message", &message); 506 papiAttributeListAddString(&svc->attributes, PAPI_ATTR_REPLACE, 507 "detailed-status-message", message); 508 } 509 papiAttributeListGetInteger(response, NULL, "status-code", &status); 510 511 return (ipp_to_papi_status(status)); 512 } 513 514 papi_status_t 515 ipp_send_request_with_file(service_t *svc, papi_attribute_t **request, 516 papi_attribute_t ***response, char *file) 517 { 518 papi_status_t result = PAPI_OK; 519 ssize_t size = 0; 520 struct stat statbuf; 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 (strcmp("standard input", file) != 0) { 539 if (stat(file, &statbuf) < 0) { 540 detailed_error(svc, 541 gettext("Cannot access file: %s: %s"), 542 file, strerror(errno)); 543 return (PAPI_DOCUMENT_ACCESS_ERROR); 544 } 545 if (statbuf.st_size == 0) { 546 detailed_error(svc, 547 "Zero byte (empty) file: %s", file); 548 return (PAPI_BAD_ARGUMENT); 549 } 550 } else if (svc->transfer_encoding != 551 TRANSFER_ENCODING_CHUNKED) { 552 struct stat st; 553 554 if (fstat(fd, &st) >= 0) 555 size = st.st_size; 556 } 557 } 558 559 *response = NULL; 560 while (*response == NULL) { 561 http_status_t status = HTTP_CONTINUE; 562 563 result = ipp_send_initial_request_block(svc, request, size); 564 565 if (result == PAPI_OK) { 566 if (file != NULL) { 567 /* send the file contents if we have it */ 568 int rc; 569 char buf[BUFSIZ]; 570 571 lseek(fd, 0L, SEEK_SET); 572 while ((rc = read(fd, buf, sizeof (buf))) > 0) { 573 if (ipp_request_write(svc, buf, rc) 574 < rc) { 575 break; 576 } 577 } 578 } 579 580 (void) ipp_request_write(svc, "", 0); 581 } 582 583 /* update our connection info */ 584 while (status == HTTP_CONTINUE) 585 status = httpUpdate(svc->connection); 586 587 if (status == HTTP_UNAUTHORIZED) { 588 httpFlush(svc->connection); 589 if ((svc->connection->authstring[0] == '\0') && 590 (setAuthString(svc) == 0)) { 591 httpReconnect(svc->connection); 592 continue; 593 } 594 } else if (status == HTTP_UPGRADE_REQUIRED) { 595 /* 596 * If the transport was built with TLS support, we can 597 * try to use it. 598 */ 599 httpFlush(svc->connection); 600 httpReconnect(svc->connection); 601 httpEncryption(svc->connection, HTTP_ENCRYPT_REQUIRED); 602 continue; 603 } 604 605 if (status != HTTP_OK) 606 return (http_to_papi_status(status)); 607 608 /* read the IPP response */ 609 result = ipp_read_message(&ipp_request_read, svc, response, 610 IPP_TYPE_RESPONSE); 611 612 if (result == PAPI_OK) 613 result = ipp_status_info(svc, *response); 614 #ifdef DEBUG 615 fprintf(stderr, "\nIPP-RESPONSE: (%s) (%s)", (file ? file : ""), 616 papiStatusString(result)); 617 papiAttributeListPrint(stderr, *response, " "); 618 putc('\n', stderr); 619 fflush(stderr); 620 #endif 621 } 622 623 return (result); 624 } 625 626 papi_status_t 627 ipp_send_request(service_t *svc, papi_attribute_t **request, 628 papi_attribute_t ***response) 629 { 630 return (ipp_send_request_with_file(svc, request, response, NULL)); 631 } 632