1 /* 2 * IPP utilities for CUPS. 3 * 4 * Copyright 2007-2014 by Apple Inc. 5 * Copyright 1997-2007 by Easy Software Products. 6 * 7 * These coded instructions, statements, and computer programs are the 8 * property of Apple Inc. and are protected by Federal copyright 9 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 10 * which should have been included with this file. If this file is 11 * missing or damaged, see the license at "http://www.cups.org/". 12 * 13 * This file is subject to the Apple OS-Developed Software exception. 14 */ 15 16 /* 17 * Include necessary headers... 18 */ 19 20 #include "cups-private.h" 21 #include <fcntl.h> 22 #include <sys/stat.h> 23 #if defined(WIN32) || defined(__EMX__) 24 # include <io.h> 25 #else 26 # include <unistd.h> 27 #endif /* WIN32 || __EMX__ */ 28 #ifndef O_BINARY 29 # define O_BINARY 0 30 #endif /* O_BINARY */ 31 #ifndef MSG_DONTWAIT 32 # define MSG_DONTWAIT 0 33 #endif /* !MSG_DONTWAIT */ 34 35 36 /* 37 * 'cupsDoFileRequest()' - Do an IPP request with a file. 38 * 39 * This function sends the IPP request and attached file to the specified 40 * server, retrying and authenticating as necessary. The request is freed with 41 * @link ippDelete@. 42 */ 43 44 ipp_t * /* O - Response data */ 45 cupsDoFileRequest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 46 ipp_t *request, /* I - IPP request */ 47 const char *resource, /* I - HTTP resource for POST */ 48 const char *filename) /* I - File to send or @code NULL@ for none */ 49 { 50 ipp_t *response; /* IPP response data */ 51 int infile; /* Input file */ 52 53 54 DEBUG_printf(("cupsDoFileRequest(http=%p, request=%p(%s), resource=\"%s\", filename=\"%s\")", (void *)http, (void *)request, request ? ippOpString(request->request.op.operation_id) : "?", resource, filename)); 55 56 if (filename) 57 { 58 if ((infile = open(filename, O_RDONLY | O_BINARY)) < 0) 59 { 60 /* 61 * Can't get file information! 62 */ 63 64 _cupsSetError(errno == ENOENT ? IPP_STATUS_ERROR_NOT_FOUND : IPP_STATUS_ERROR_NOT_AUTHORIZED, 65 NULL, 0); 66 67 ippDelete(request); 68 69 return (NULL); 70 } 71 } 72 else 73 infile = -1; 74 75 response = cupsDoIORequest(http, request, resource, infile, -1); 76 77 if (infile >= 0) 78 close(infile); 79 80 return (response); 81 } 82 83 84 /* 85 * 'cupsDoIORequest()' - Do an IPP request with file descriptors. 86 * 87 * This function sends the IPP request with the optional input file "infile" to 88 * the specified server, retrying and authenticating as necessary. The request 89 * is freed with @link ippDelete@. 90 * 91 * If "infile" is a valid file descriptor, @code cupsDoIORequest@ copies 92 * all of the data from the file after the IPP request message. 93 * 94 * If "outfile" is a valid file descriptor, @code cupsDoIORequest@ copies 95 * all of the data after the IPP response message to the file. 96 * 97 * @since CUPS 1.3/macOS 10.5@ 98 */ 99 100 ipp_t * /* O - Response data */ 101 cupsDoIORequest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 102 ipp_t *request, /* I - IPP request */ 103 const char *resource, /* I - HTTP resource for POST */ 104 int infile, /* I - File to read from or -1 for none */ 105 int outfile) /* I - File to write to or -1 for none */ 106 { 107 ipp_t *response = NULL; /* IPP response data */ 108 size_t length = 0; /* Content-Length value */ 109 http_status_t status; /* Status of HTTP request */ 110 struct stat fileinfo; /* File information */ 111 ssize_t bytes; /* Number of bytes read/written */ 112 char buffer[32768]; /* Output buffer */ 113 114 115 DEBUG_printf(("cupsDoIORequest(http=%p, request=%p(%s), resource=\"%s\", infile=%d, outfile=%d)", (void *)http, (void *)request, request ? ippOpString(request->request.op.operation_id) : "?", resource, infile, outfile)); 116 117 /* 118 * Range check input... 119 */ 120 121 if (!request || !resource) 122 { 123 ippDelete(request); 124 125 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 126 127 return (NULL); 128 } 129 130 /* 131 * Get the default connection as needed... 132 */ 133 134 if (!http) 135 if ((http = _cupsConnect()) == NULL) 136 { 137 ippDelete(request); 138 139 return (NULL); 140 } 141 142 /* 143 * See if we have a file to send... 144 */ 145 146 if (infile >= 0) 147 { 148 if (fstat(infile, &fileinfo)) 149 { 150 /* 151 * Can't get file information! 152 */ 153 154 _cupsSetError(errno == EBADF ? IPP_STATUS_ERROR_NOT_FOUND : IPP_STATUS_ERROR_NOT_AUTHORIZED, 155 NULL, 0); 156 157 ippDelete(request); 158 159 return (NULL); 160 } 161 162 #ifdef WIN32 163 if (fileinfo.st_mode & _S_IFDIR) 164 #else 165 if (S_ISDIR(fileinfo.st_mode)) 166 #endif /* WIN32 */ 167 { 168 /* 169 * Can't send a directory... 170 */ 171 172 ippDelete(request); 173 174 _cupsSetError(IPP_STATUS_ERROR_NOT_POSSIBLE, strerror(EISDIR), 0); 175 176 return (NULL); 177 } 178 179 #ifndef WIN32 180 if (!S_ISREG(fileinfo.st_mode)) 181 length = 0; /* Chunk when piping */ 182 else 183 #endif /* !WIN32 */ 184 length = ippLength(request) + (size_t)fileinfo.st_size; 185 } 186 else 187 length = ippLength(request); 188 189 DEBUG_printf(("2cupsDoIORequest: Request length=%ld, total length=%ld", 190 (long)ippLength(request), (long)length)); 191 192 /* 193 * Clear any "Local" authentication data since it is probably stale... 194 */ 195 196 if (http->authstring && !strncmp(http->authstring, "Local ", 6)) 197 httpSetAuthString(http, NULL, NULL); 198 199 /* 200 * Loop until we can send the request without authorization problems. 201 */ 202 203 while (response == NULL) 204 { 205 DEBUG_puts("2cupsDoIORequest: setup..."); 206 207 /* 208 * Send the request... 209 */ 210 211 status = cupsSendRequest(http, request, resource, length); 212 213 DEBUG_printf(("2cupsDoIORequest: status=%d", status)); 214 215 if (status == HTTP_STATUS_CONTINUE && request->state == IPP_STATE_DATA && infile >= 0) 216 { 217 DEBUG_puts("2cupsDoIORequest: file write..."); 218 219 /* 220 * Send the file with the request... 221 */ 222 223 #ifndef WIN32 224 if (S_ISREG(fileinfo.st_mode)) 225 #endif /* WIN32 */ 226 lseek(infile, 0, SEEK_SET); 227 228 while ((bytes = read(infile, buffer, sizeof(buffer))) > 0) 229 { 230 if ((status = cupsWriteRequestData(http, buffer, (size_t)bytes)) 231 != HTTP_STATUS_CONTINUE) 232 break; 233 } 234 } 235 236 /* 237 * Get the server's response... 238 */ 239 240 if (status <= HTTP_STATUS_CONTINUE || status == HTTP_STATUS_OK) 241 { 242 response = cupsGetResponse(http, resource); 243 status = httpGetStatus(http); 244 } 245 246 DEBUG_printf(("2cupsDoIORequest: status=%d", status)); 247 248 if (status == HTTP_STATUS_ERROR || 249 (status >= HTTP_STATUS_BAD_REQUEST && status != HTTP_STATUS_UNAUTHORIZED && 250 status != HTTP_STATUS_UPGRADE_REQUIRED)) 251 { 252 _cupsSetHTTPError(status); 253 break; 254 } 255 256 if (response && outfile >= 0) 257 { 258 /* 259 * Write trailing data to file... 260 */ 261 262 while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0) 263 if (write(outfile, buffer, (size_t)bytes) < bytes) 264 break; 265 } 266 267 if (http->state != HTTP_STATE_WAITING) 268 { 269 /* 270 * Flush any remaining data... 271 */ 272 273 httpFlush(http); 274 } 275 } 276 277 /* 278 * Delete the original request and return the response... 279 */ 280 281 ippDelete(request); 282 283 return (response); 284 } 285 286 287 /* 288 * 'cupsDoRequest()' - Do an IPP request. 289 * 290 * This function sends the IPP request to the specified server, retrying 291 * and authenticating as necessary. The request is freed with @link ippDelete@. 292 */ 293 294 ipp_t * /* O - Response data */ 295 cupsDoRequest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 296 ipp_t *request, /* I - IPP request */ 297 const char *resource) /* I - HTTP resource for POST */ 298 { 299 DEBUG_printf(("cupsDoRequest(http=%p, request=%p(%s), resource=\"%s\")", (void *)http, (void *)request, request ? ippOpString(request->request.op.operation_id) : "?", resource)); 300 301 return (cupsDoIORequest(http, request, resource, -1, -1)); 302 } 303 304 305 /* 306 * 'cupsGetResponse()' - Get a response to an IPP request. 307 * 308 * Use this function to get the response for an IPP request sent using 309 * @link cupsSendRequest@. For requests that return additional data, use 310 * @link cupsReadResponseData@ after getting a successful response, 311 * otherwise call @link httpFlush@ to complete the response processing. 312 * 313 * @since CUPS 1.4/macOS 10.6@ 314 */ 315 316 ipp_t * /* O - Response or @code NULL@ on HTTP error */ 317 cupsGetResponse(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 318 const char *resource) /* I - HTTP resource for POST */ 319 { 320 http_status_t status; /* HTTP status */ 321 ipp_state_t state; /* IPP read state */ 322 ipp_t *response = NULL; /* IPP response */ 323 324 325 DEBUG_printf(("cupsGetResponse(http=%p, resource=\"%s\")", (void *)http, resource)); 326 DEBUG_printf(("1cupsGetResponse: http->state=%d", http ? http->state : HTTP_STATE_ERROR)); 327 328 /* 329 * Connect to the default server as needed... 330 */ 331 332 if (!http) 333 { 334 _cups_globals_t *cg = _cupsGlobals(); 335 /* Pointer to library globals */ 336 337 if ((http = cg->http) == NULL) 338 { 339 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No active connection."), 1); 340 DEBUG_puts("1cupsGetResponse: No active connection - returning NULL."); 341 return (NULL); 342 } 343 } 344 345 if (http->state != HTTP_STATE_POST_RECV && http->state != HTTP_STATE_POST_SEND) 346 { 347 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No request sent."), 1); 348 DEBUG_puts("1cupsGetResponse: Not in POST state - returning NULL."); 349 return (NULL); 350 } 351 352 /* 353 * Check for an unfinished chunked request... 354 */ 355 356 if (http->data_encoding == HTTP_ENCODING_CHUNKED) 357 { 358 /* 359 * Send a 0-length chunk to finish off the request... 360 */ 361 362 DEBUG_puts("2cupsGetResponse: Finishing chunked POST..."); 363 364 if (httpWrite2(http, "", 0) < 0) 365 return (NULL); 366 } 367 368 /* 369 * Wait for a response from the server... 370 */ 371 372 DEBUG_printf(("2cupsGetResponse: Update loop, http->status=%d...", 373 http->status)); 374 375 do 376 { 377 status = httpUpdate(http); 378 } 379 while (status == HTTP_STATUS_CONTINUE); 380 381 DEBUG_printf(("2cupsGetResponse: status=%d", status)); 382 383 if (status == HTTP_STATUS_OK) 384 { 385 /* 386 * Get the IPP response... 387 */ 388 389 response = ippNew(); 390 391 while ((state = ippRead(http, response)) != IPP_STATE_DATA) 392 if (state == IPP_STATE_ERROR) 393 break; 394 395 if (state == IPP_STATE_ERROR) 396 { 397 /* 398 * Flush remaining data and delete the response... 399 */ 400 401 DEBUG_puts("1cupsGetResponse: IPP read error!"); 402 403 httpFlush(http); 404 405 ippDelete(response); 406 response = NULL; 407 408 http->status = status = HTTP_STATUS_ERROR; 409 http->error = EINVAL; 410 } 411 } 412 else if (status != HTTP_STATUS_ERROR) 413 { 414 /* 415 * Flush any error message... 416 */ 417 418 httpFlush(http); 419 420 /* 421 * Then handle encryption and authentication... 422 */ 423 424 if (status == HTTP_STATUS_UNAUTHORIZED) 425 { 426 /* 427 * See if we can do authentication... 428 */ 429 430 DEBUG_puts("2cupsGetResponse: Need authorization..."); 431 432 if (!cupsDoAuthentication(http, "POST", resource)) 433 httpReconnect2(http, 30000, NULL); 434 else 435 http->status = status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED; 436 } 437 438 #ifdef HAVE_SSL 439 else if (status == HTTP_STATUS_UPGRADE_REQUIRED) 440 { 441 /* 442 * Force a reconnect with encryption... 443 */ 444 445 DEBUG_puts("2cupsGetResponse: Need encryption..."); 446 447 if (!httpReconnect2(http, 30000, NULL)) 448 httpEncryption(http, HTTP_ENCRYPTION_REQUIRED); 449 } 450 #endif /* HAVE_SSL */ 451 } 452 453 if (response) 454 { 455 ipp_attribute_t *attr; /* status-message attribute */ 456 457 458 attr = ippFindAttribute(response, "status-message", IPP_TAG_TEXT); 459 460 DEBUG_printf(("1cupsGetResponse: status-code=%s, status-message=\"%s\"", 461 ippErrorString(response->request.status.status_code), 462 attr ? attr->values[0].string.text : "")); 463 464 _cupsSetError(response->request.status.status_code, 465 attr ? attr->values[0].string.text : 466 ippErrorString(response->request.status.status_code), 0); 467 } 468 469 return (response); 470 } 471 472 473 /* 474 * 'cupsLastError()' - Return the last IPP status code received on the current 475 * thread. 476 */ 477 478 ipp_status_t /* O - IPP status code from last request */ 479 cupsLastError(void) 480 { 481 return (_cupsGlobals()->last_error); 482 } 483 484 485 /* 486 * 'cupsLastErrorString()' - Return the last IPP status-message received on the 487 * current thread. 488 * 489 * @since CUPS 1.2/macOS 10.5@ 490 */ 491 492 const char * /* O - status-message text from last request */ 493 cupsLastErrorString(void) 494 { 495 return (_cupsGlobals()->last_status_message); 496 } 497 498 499 /* 500 * '_cupsNextDelay()' - Return the next retry delay value. 501 * 502 * This function currently returns the Fibonacci sequence 1 1 2 3 5 8. 503 * 504 * Pass 0 for the current delay value to initialize the sequence. 505 */ 506 507 int /* O - Next delay value */ 508 _cupsNextDelay(int current, /* I - Current delay value or 0 */ 509 int *previous) /* IO - Previous delay value */ 510 { 511 int next; /* Next delay value */ 512 513 514 if (current > 0) 515 { 516 next = (current + *previous) % 12; 517 *previous = next < current ? 0 : current; 518 } 519 else 520 { 521 next = 1; 522 *previous = 0; 523 } 524 525 return (next); 526 } 527 528 529 /* 530 * 'cupsReadResponseData()' - Read additional data after the IPP response. 531 * 532 * This function is used after @link cupsGetResponse@ to read the PPD or document 533 * files from @code CUPS_GET_PPD@ and @code CUPS_GET_DOCUMENT@ requests, 534 * respectively. 535 * 536 * @since CUPS 1.4/macOS 10.6@ 537 */ 538 539 ssize_t /* O - Bytes read, 0 on EOF, -1 on error */ 540 cupsReadResponseData( 541 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 542 char *buffer, /* I - Buffer to use */ 543 size_t length) /* I - Number of bytes to read */ 544 { 545 /* 546 * Get the default connection as needed... 547 */ 548 549 DEBUG_printf(("cupsReadResponseData(http=%p, buffer=%p, length=" CUPS_LLFMT ")", (void *)http, (void *)buffer, CUPS_LLCAST length)); 550 551 if (!http) 552 { 553 _cups_globals_t *cg = _cupsGlobals(); 554 /* Pointer to library globals */ 555 556 if ((http = cg->http) == NULL) 557 { 558 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No active connection"), 1); 559 return (-1); 560 } 561 } 562 563 /* 564 * Then read from the HTTP connection... 565 */ 566 567 return (httpRead2(http, buffer, length)); 568 } 569 570 571 /* 572 * 'cupsSendRequest()' - Send an IPP request. 573 * 574 * Use @link cupsWriteRequestData@ to write any additional data (document, PPD 575 * file, etc.) for the request, @link cupsGetResponse@ to get the IPP response, 576 * and @link cupsReadResponseData@ to read any additional data following the 577 * response. Only one request can be sent/queued at a time per @code http_t@ 578 * connection. 579 * 580 * Returns the initial HTTP status code, which will be @code HTTP_STATUS_CONTINUE@ 581 * on a successful send of the request. 582 * 583 * Note: Unlike @link cupsDoFileRequest@, @link cupsDoIORequest@, and 584 * @link cupsDoRequest@, the request is NOT freed with @link ippDelete@. 585 * 586 * @since CUPS 1.4/macOS 10.6@ 587 */ 588 589 http_status_t /* O - Initial HTTP status */ 590 cupsSendRequest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 591 ipp_t *request, /* I - IPP request */ 592 const char *resource, /* I - Resource path */ 593 size_t length) /* I - Length of data to follow or @code CUPS_LENGTH_VARIABLE@ */ 594 { 595 http_status_t status; /* Status of HTTP request */ 596 int got_status; /* Did we get the status? */ 597 ipp_state_t state; /* State of IPP processing */ 598 http_status_t expect; /* Expect: header to use */ 599 600 601 DEBUG_printf(("cupsSendRequest(http=%p, request=%p(%s), resource=\"%s\", length=" CUPS_LLFMT ")", (void *)http, (void *)request, request ? ippOpString(request->request.op.operation_id) : "?", resource, CUPS_LLCAST length)); 602 603 /* 604 * Range check input... 605 */ 606 607 if (!request || !resource) 608 { 609 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 610 611 return (HTTP_STATUS_ERROR); 612 } 613 614 /* 615 * Get the default connection as needed... 616 */ 617 618 if (!http) 619 if ((http = _cupsConnect()) == NULL) 620 return (HTTP_STATUS_SERVICE_UNAVAILABLE); 621 622 /* 623 * If the prior request was not flushed out, do so now... 624 */ 625 626 if (http->state == HTTP_STATE_GET_SEND || 627 http->state == HTTP_STATE_POST_SEND) 628 { 629 DEBUG_puts("2cupsSendRequest: Flush prior response."); 630 httpFlush(http); 631 } 632 else if (http->state != HTTP_STATE_WAITING) 633 { 634 DEBUG_printf(("1cupsSendRequest: Unknown HTTP state (%d), " 635 "reconnecting.", http->state)); 636 if (httpReconnect2(http, 30000, NULL)) 637 return (HTTP_STATUS_ERROR); 638 } 639 640 #ifdef HAVE_SSL 641 /* 642 * See if we have an auth-info attribute and are communicating over 643 * a non-local link. If so, encrypt the link so that we can pass 644 * the authentication information securely... 645 */ 646 647 if (ippFindAttribute(request, "auth-info", IPP_TAG_TEXT) && 648 !httpAddrLocalhost(http->hostaddr) && !http->tls && 649 httpEncryption(http, HTTP_ENCRYPTION_REQUIRED)) 650 { 651 DEBUG_puts("1cupsSendRequest: Unable to encrypt connection."); 652 return (HTTP_STATUS_SERVICE_UNAVAILABLE); 653 } 654 #endif /* HAVE_SSL */ 655 656 /* 657 * Reconnect if the last response had a "Connection: close"... 658 */ 659 660 if (!_cups_strcasecmp(http->fields[HTTP_FIELD_CONNECTION], "close")) 661 { 662 DEBUG_puts("2cupsSendRequest: Connection: close"); 663 httpClearFields(http); 664 if (httpReconnect2(http, 30000, NULL)) 665 { 666 DEBUG_puts("1cupsSendRequest: Unable to reconnect."); 667 return (HTTP_STATUS_SERVICE_UNAVAILABLE); 668 } 669 } 670 671 /* 672 * Loop until we can send the request without authorization problems. 673 */ 674 675 expect = HTTP_STATUS_CONTINUE; 676 677 for (;;) 678 { 679 DEBUG_puts("2cupsSendRequest: Setup..."); 680 681 /* 682 * Setup the HTTP variables needed... 683 */ 684 685 httpClearFields(http); 686 httpSetExpect(http, expect); 687 httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp"); 688 httpSetLength(http, length); 689 690 #ifdef HAVE_GSSAPI 691 if (http->authstring && !strncmp(http->authstring, "Negotiate", 9)) 692 { 693 /* 694 * Do not use cached Kerberos credentials since they will look like a 695 * "replay" attack... 696 */ 697 698 _cupsSetNegotiateAuthString(http, "POST", resource); 699 } 700 #endif /* HAVE_GSSAPI */ 701 702 httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring); 703 704 DEBUG_printf(("2cupsSendRequest: authstring=\"%s\"", http->authstring)); 705 706 /* 707 * Try the request... 708 */ 709 710 DEBUG_puts("2cupsSendRequest: Sending HTTP POST..."); 711 712 if (httpPost(http, resource)) 713 { 714 DEBUG_puts("2cupsSendRequest: POST failed, reconnecting."); 715 if (httpReconnect2(http, 30000, NULL)) 716 { 717 DEBUG_puts("1cupsSendRequest: Unable to reconnect."); 718 return (HTTP_STATUS_SERVICE_UNAVAILABLE); 719 } 720 else 721 continue; 722 } 723 724 /* 725 * Send the IPP data... 726 */ 727 728 DEBUG_puts("2cupsSendRequest: Writing IPP request..."); 729 730 request->state = IPP_STATE_IDLE; 731 status = HTTP_STATUS_CONTINUE; 732 got_status = 0; 733 734 while ((state = ippWrite(http, request)) != IPP_STATE_DATA) 735 { 736 if (httpCheck(http)) 737 { 738 got_status = 1; 739 740 _httpUpdate(http, &status); 741 if (status >= HTTP_STATUS_MULTIPLE_CHOICES) 742 break; 743 } 744 else if (state == IPP_STATE_ERROR) 745 break; 746 } 747 748 if (state == IPP_STATE_ERROR) 749 { 750 /* 751 * We weren't able to send the IPP request. But did we already get a HTTP 752 * error status? 753 */ 754 755 if (!got_status || status < HTTP_STATUS_MULTIPLE_CHOICES) 756 { 757 /* 758 * No, something else went wrong. 759 */ 760 761 DEBUG_puts("1cupsSendRequest: Unable to send IPP request."); 762 763 http->status = HTTP_STATUS_ERROR; 764 http->state = HTTP_STATE_WAITING; 765 766 return (HTTP_STATUS_ERROR); 767 } 768 } 769 770 /* 771 * Wait up to 1 second to get the 100-continue response as needed... 772 */ 773 774 if (!got_status) 775 { 776 if (expect == HTTP_STATUS_CONTINUE) 777 { 778 DEBUG_puts("2cupsSendRequest: Waiting for 100-continue..."); 779 780 if (httpWait(http, 1000)) 781 _httpUpdate(http, &status); 782 } 783 else if (httpCheck(http)) 784 _httpUpdate(http, &status); 785 } 786 787 DEBUG_printf(("2cupsSendRequest: status=%d", status)); 788 789 /* 790 * Process the current HTTP status... 791 */ 792 793 if (status >= HTTP_STATUS_MULTIPLE_CHOICES) 794 { 795 int temp_status; /* Temporary status */ 796 797 _cupsSetHTTPError(status); 798 799 do 800 { 801 temp_status = httpUpdate(http); 802 } 803 while (temp_status != HTTP_STATUS_ERROR && 804 http->state == HTTP_STATE_POST_RECV); 805 806 httpFlush(http); 807 } 808 809 switch (status) 810 { 811 case HTTP_STATUS_CONTINUE : 812 case HTTP_STATUS_OK : 813 case HTTP_STATUS_ERROR : 814 DEBUG_printf(("1cupsSendRequest: Returning %d.", status)); 815 return (status); 816 817 case HTTP_STATUS_UNAUTHORIZED : 818 if (cupsDoAuthentication(http, "POST", resource)) 819 { 820 DEBUG_puts("1cupsSendRequest: Returning HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED."); 821 return (HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED); 822 } 823 824 DEBUG_puts("2cupsSendRequest: Reconnecting after HTTP_STATUS_UNAUTHORIZED."); 825 826 if (httpReconnect2(http, 30000, NULL)) 827 { 828 DEBUG_puts("1cupsSendRequest: Unable to reconnect."); 829 return (HTTP_STATUS_SERVICE_UNAVAILABLE); 830 } 831 break; 832 833 #ifdef HAVE_SSL 834 case HTTP_STATUS_UPGRADE_REQUIRED : 835 /* 836 * Flush any error message, reconnect, and then upgrade with 837 * encryption... 838 */ 839 840 DEBUG_puts("2cupsSendRequest: Reconnecting after " 841 "HTTP_STATUS_UPGRADE_REQUIRED."); 842 843 if (httpReconnect2(http, 30000, NULL)) 844 { 845 DEBUG_puts("1cupsSendRequest: Unable to reconnect."); 846 return (HTTP_STATUS_SERVICE_UNAVAILABLE); 847 } 848 849 DEBUG_puts("2cupsSendRequest: Upgrading to TLS."); 850 if (httpEncryption(http, HTTP_ENCRYPTION_REQUIRED)) 851 { 852 DEBUG_puts("1cupsSendRequest: Unable to encrypt connection."); 853 return (HTTP_STATUS_SERVICE_UNAVAILABLE); 854 } 855 break; 856 #endif /* HAVE_SSL */ 857 858 case HTTP_STATUS_EXPECTATION_FAILED : 859 /* 860 * Don't try using the Expect: header the next time around... 861 */ 862 863 expect = (http_status_t)0; 864 865 DEBUG_puts("2cupsSendRequest: Reconnecting after " 866 "HTTP_EXPECTATION_FAILED."); 867 868 if (httpReconnect2(http, 30000, NULL)) 869 { 870 DEBUG_puts("1cupsSendRequest: Unable to reconnect."); 871 return (HTTP_STATUS_SERVICE_UNAVAILABLE); 872 } 873 break; 874 875 default : 876 /* 877 * Some other error... 878 */ 879 880 return (status); 881 } 882 } 883 } 884 885 886 /* 887 * 'cupsWriteRequestData()' - Write additional data after an IPP request. 888 * 889 * This function is used after @link cupsSendRequest@ to provide a PPD and 890 * after @link cupsStartDocument@ to provide a document file. 891 * 892 * @since CUPS 1.4/macOS 10.6@ 893 */ 894 895 http_status_t /* O - @code HTTP_STATUS_CONTINUE@ if OK or HTTP status on error */ 896 cupsWriteRequestData( 897 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 898 const char *buffer, /* I - Bytes to write */ 899 size_t length) /* I - Number of bytes to write */ 900 { 901 int wused; /* Previous bytes in buffer */ 902 903 904 /* 905 * Get the default connection as needed... 906 */ 907 908 DEBUG_printf(("cupsWriteRequestData(http=%p, buffer=%p, length=" CUPS_LLFMT ")", (void *)http, (void *)buffer, CUPS_LLCAST length)); 909 910 if (!http) 911 { 912 _cups_globals_t *cg = _cupsGlobals(); 913 /* Pointer to library globals */ 914 915 if ((http = cg->http) == NULL) 916 { 917 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No active connection"), 1); 918 DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_ERROR."); 919 return (HTTP_STATUS_ERROR); 920 } 921 } 922 923 /* 924 * Then write to the HTTP connection... 925 */ 926 927 wused = http->wused; 928 929 if (httpWrite2(http, buffer, length) < 0) 930 { 931 DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_ERROR."); 932 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(http->error), 0); 933 return (HTTP_STATUS_ERROR); 934 } 935 936 /* 937 * Finally, check if we have any pending data from the server... 938 */ 939 940 if (length >= HTTP_MAX_BUFFER || 941 http->wused < wused || 942 (wused > 0 && (size_t)http->wused == length)) 943 { 944 /* 945 * We've written something to the server, so check for response data... 946 */ 947 948 if (_httpWait(http, 0, 1)) 949 { 950 http_status_t status; /* Status from _httpUpdate */ 951 952 _httpUpdate(http, &status); 953 if (status >= HTTP_STATUS_MULTIPLE_CHOICES) 954 { 955 _cupsSetHTTPError(status); 956 957 do 958 { 959 status = httpUpdate(http); 960 } 961 while (status != HTTP_STATUS_ERROR && http->state == HTTP_STATE_POST_RECV); 962 963 httpFlush(http); 964 } 965 966 DEBUG_printf(("1cupsWriteRequestData: Returning %d.\n", status)); 967 return (status); 968 } 969 } 970 971 DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_CONTINUE."); 972 return (HTTP_STATUS_CONTINUE); 973 } 974 975 976 /* 977 * '_cupsConnect()' - Get the default server connection... 978 */ 979 980 http_t * /* O - HTTP connection */ 981 _cupsConnect(void) 982 { 983 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */ 984 985 986 /* 987 * See if we are connected to the same server... 988 */ 989 990 if (cg->http) 991 { 992 /* 993 * Compare the connection hostname, port, and encryption settings to 994 * the cached defaults; these were initialized the first time we 995 * connected... 996 */ 997 998 if (strcmp(cg->http->hostname, cg->server) || 999 cg->ipp_port != httpAddrPort(cg->http->hostaddr) || 1000 (cg->http->encryption != cg->encryption && 1001 cg->http->encryption == HTTP_ENCRYPTION_NEVER)) 1002 { 1003 /* 1004 * Need to close the current connection because something has changed... 1005 */ 1006 1007 httpClose(cg->http); 1008 cg->http = NULL; 1009 } 1010 else 1011 { 1012 /* 1013 * Same server, see if the connection is still established... 1014 */ 1015 1016 char ch; /* Connection check byte */ 1017 ssize_t n; /* Number of bytes */ 1018 1019 #ifdef WIN32 1020 if ((n = recv(cg->http->fd, &ch, 1, MSG_PEEK)) == 0 || 1021 (n < 0 && WSAGetLastError() != WSAEWOULDBLOCK)) 1022 #else 1023 if ((n = recv(cg->http->fd, &ch, 1, MSG_PEEK | MSG_DONTWAIT)) == 0 || 1024 (n < 0 && errno != EWOULDBLOCK)) 1025 #endif /* WIN32 */ 1026 { 1027 /* 1028 * Nope, close the connection... 1029 */ 1030 1031 httpClose(cg->http); 1032 cg->http = NULL; 1033 } 1034 } 1035 } 1036 1037 /* 1038 * (Re)connect as needed... 1039 */ 1040 1041 if (!cg->http) 1042 { 1043 if ((cg->http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC, 1044 cupsEncryption(), 1, 30000, NULL)) == NULL) 1045 { 1046 if (errno) 1047 _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, NULL, 0); 1048 else 1049 _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, 1050 _("Unable to connect to host."), 1); 1051 } 1052 } 1053 1054 /* 1055 * Return the cached connection... 1056 */ 1057 1058 return (cg->http); 1059 } 1060 1061 1062 /* 1063 * '_cupsSetError()' - Set the last IPP status code and status-message. 1064 */ 1065 1066 void 1067 _cupsSetError(ipp_status_t status, /* I - IPP status code */ 1068 const char *message, /* I - status-message value */ 1069 int localize) /* I - Localize the message? */ 1070 { 1071 _cups_globals_t *cg; /* Global data */ 1072 1073 1074 if (!message && errno) 1075 { 1076 message = strerror(errno); 1077 localize = 0; 1078 } 1079 1080 cg = _cupsGlobals(); 1081 cg->last_error = status; 1082 1083 if (cg->last_status_message) 1084 { 1085 _cupsStrFree(cg->last_status_message); 1086 1087 cg->last_status_message = NULL; 1088 } 1089 1090 if (message) 1091 { 1092 if (localize) 1093 { 1094 /* 1095 * Get the message catalog... 1096 */ 1097 1098 if (!cg->lang_default) 1099 cg->lang_default = cupsLangDefault(); 1100 1101 cg->last_status_message = _cupsStrAlloc(_cupsLangString(cg->lang_default, 1102 message)); 1103 } 1104 else 1105 cg->last_status_message = _cupsStrAlloc(message); 1106 } 1107 1108 DEBUG_printf(("4_cupsSetError: last_error=%s, last_status_message=\"%s\"", 1109 ippErrorString(cg->last_error), cg->last_status_message)); 1110 } 1111 1112 1113 /* 1114 * '_cupsSetHTTPError()' - Set the last error using the HTTP status. 1115 */ 1116 1117 void 1118 _cupsSetHTTPError(http_status_t status) /* I - HTTP status code */ 1119 { 1120 switch (status) 1121 { 1122 case HTTP_STATUS_NOT_FOUND : 1123 _cupsSetError(IPP_STATUS_ERROR_NOT_FOUND, httpStatus(status), 0); 1124 break; 1125 1126 case HTTP_STATUS_UNAUTHORIZED : 1127 _cupsSetError(IPP_STATUS_ERROR_NOT_AUTHENTICATED, httpStatus(status), 0); 1128 break; 1129 1130 case HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED : 1131 _cupsSetError(IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED, httpStatus(status), 0); 1132 break; 1133 1134 case HTTP_STATUS_FORBIDDEN : 1135 _cupsSetError(IPP_STATUS_ERROR_FORBIDDEN, httpStatus(status), 0); 1136 break; 1137 1138 case HTTP_STATUS_BAD_REQUEST : 1139 _cupsSetError(IPP_STATUS_ERROR_BAD_REQUEST, httpStatus(status), 0); 1140 break; 1141 1142 case HTTP_STATUS_REQUEST_TOO_LARGE : 1143 _cupsSetError(IPP_STATUS_ERROR_REQUEST_VALUE, httpStatus(status), 0); 1144 break; 1145 1146 case HTTP_STATUS_NOT_IMPLEMENTED : 1147 _cupsSetError(IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED, httpStatus(status), 0); 1148 break; 1149 1150 case HTTP_STATUS_NOT_SUPPORTED : 1151 _cupsSetError(IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED, httpStatus(status), 0); 1152 break; 1153 1154 case HTTP_STATUS_UPGRADE_REQUIRED : 1155 _cupsSetError(IPP_STATUS_ERROR_CUPS_UPGRADE_REQUIRED, httpStatus(status), 0); 1156 break; 1157 1158 case HTTP_STATUS_CUPS_PKI_ERROR : 1159 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, httpStatus(status), 0); 1160 break; 1161 1162 case HTTP_STATUS_ERROR : 1163 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); 1164 break; 1165 1166 default : 1167 DEBUG_printf(("4_cupsSetHTTPError: HTTP error %d mapped to " 1168 "IPP_STATUS_ERROR_SERVICE_UNAVAILABLE!", status)); 1169 _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, httpStatus(status), 0); 1170 break; 1171 } 1172 } 1173