1 /* 2 * IPP utilities for CUPS. 3 * 4 * Copyright 2007-2017 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 && (http = _cupsConnect()) == NULL) 135 { 136 ippDelete(request); 137 138 return (NULL); 139 } 140 141 /* 142 * See if we have a file to send... 143 */ 144 145 if (infile >= 0) 146 { 147 if (fstat(infile, &fileinfo)) 148 { 149 /* 150 * Can't get file information! 151 */ 152 153 _cupsSetError(errno == EBADF ? IPP_STATUS_ERROR_NOT_FOUND : IPP_STATUS_ERROR_NOT_AUTHORIZED, NULL, 0); 154 ippDelete(request); 155 156 return (NULL); 157 } 158 159 #ifdef WIN32 160 if (fileinfo.st_mode & _S_IFDIR) 161 #else 162 if (S_ISDIR(fileinfo.st_mode)) 163 #endif /* WIN32 */ 164 { 165 /* 166 * Can't send a directory... 167 */ 168 169 _cupsSetError(IPP_STATUS_ERROR_NOT_POSSIBLE, strerror(EISDIR), 0); 170 ippDelete(request); 171 172 return (NULL); 173 } 174 175 #ifndef WIN32 176 if (!S_ISREG(fileinfo.st_mode)) 177 length = 0; /* Chunk when piping */ 178 else 179 #endif /* !WIN32 */ 180 length = ippLength(request) + (size_t)fileinfo.st_size; 181 } 182 else 183 length = ippLength(request); 184 185 DEBUG_printf(("2cupsDoIORequest: Request length=%ld, total length=%ld", (long)ippLength(request), (long)length)); 186 187 /* 188 * Clear any "Local" authentication data since it is probably stale... 189 */ 190 191 if (http->authstring && !strncmp(http->authstring, "Local ", 6)) 192 httpSetAuthString(http, NULL, NULL); 193 194 /* 195 * Loop until we can send the request without authorization problems. 196 */ 197 198 while (response == NULL) 199 { 200 DEBUG_puts("2cupsDoIORequest: setup..."); 201 202 /* 203 * Send the request... 204 */ 205 206 status = cupsSendRequest(http, request, resource, length); 207 208 DEBUG_printf(("2cupsDoIORequest: status=%d", status)); 209 210 if (status == HTTP_STATUS_CONTINUE && request->state == IPP_STATE_DATA && infile >= 0) 211 { 212 DEBUG_puts("2cupsDoIORequest: file write..."); 213 214 /* 215 * Send the file with the request... 216 */ 217 218 #ifndef WIN32 219 if (S_ISREG(fileinfo.st_mode)) 220 #endif /* WIN32 */ 221 lseek(infile, 0, SEEK_SET); 222 223 while ((bytes = read(infile, buffer, sizeof(buffer))) > 0) 224 { 225 if ((status = cupsWriteRequestData(http, buffer, (size_t)bytes)) 226 != HTTP_STATUS_CONTINUE) 227 break; 228 } 229 } 230 231 /* 232 * Get the server's response... 233 */ 234 235 if (status <= HTTP_STATUS_CONTINUE || status == HTTP_STATUS_OK) 236 { 237 response = cupsGetResponse(http, resource); 238 status = httpGetStatus(http); 239 } 240 241 DEBUG_printf(("2cupsDoIORequest: status=%d", status)); 242 243 if (status == HTTP_STATUS_ERROR || 244 (status >= HTTP_STATUS_BAD_REQUEST && status != HTTP_STATUS_UNAUTHORIZED && 245 status != HTTP_STATUS_UPGRADE_REQUIRED)) 246 { 247 _cupsSetHTTPError(status); 248 break; 249 } 250 251 if (response && outfile >= 0) 252 { 253 /* 254 * Write trailing data to file... 255 */ 256 257 while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0) 258 if (write(outfile, buffer, (size_t)bytes) < bytes) 259 break; 260 } 261 262 if (http->state != HTTP_STATE_WAITING) 263 { 264 /* 265 * Flush any remaining data... 266 */ 267 268 httpFlush(http); 269 } 270 } 271 272 /* 273 * Delete the original request and return the response... 274 */ 275 276 ippDelete(request); 277 278 return (response); 279 } 280 281 282 /* 283 * 'cupsDoRequest()' - Do an IPP request. 284 * 285 * This function sends the IPP request to the specified server, retrying 286 * and authenticating as necessary. The request is freed with @link ippDelete@. 287 */ 288 289 ipp_t * /* O - Response data */ 290 cupsDoRequest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 291 ipp_t *request, /* I - IPP request */ 292 const char *resource) /* I - HTTP resource for POST */ 293 { 294 DEBUG_printf(("cupsDoRequest(http=%p, request=%p(%s), resource=\"%s\")", (void *)http, (void *)request, request ? ippOpString(request->request.op.operation_id) : "?", resource)); 295 296 return (cupsDoIORequest(http, request, resource, -1, -1)); 297 } 298 299 300 /* 301 * 'cupsGetResponse()' - Get a response to an IPP request. 302 * 303 * Use this function to get the response for an IPP request sent using 304 * @link cupsSendRequest@. For requests that return additional data, use 305 * @link cupsReadResponseData@ after getting a successful response, 306 * otherwise call @link httpFlush@ to complete the response processing. 307 * 308 * @since CUPS 1.4/macOS 10.6@ 309 */ 310 311 ipp_t * /* O - Response or @code NULL@ on HTTP error */ 312 cupsGetResponse(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 313 const char *resource) /* I - HTTP resource for POST */ 314 { 315 http_status_t status; /* HTTP status */ 316 ipp_state_t state; /* IPP read state */ 317 ipp_t *response = NULL; /* IPP response */ 318 319 320 DEBUG_printf(("cupsGetResponse(http=%p, resource=\"%s\")", (void *)http, resource)); 321 DEBUG_printf(("1cupsGetResponse: http->state=%d", http ? http->state : HTTP_STATE_ERROR)); 322 323 /* 324 * Connect to the default server as needed... 325 */ 326 327 if (!http) 328 { 329 _cups_globals_t *cg = _cupsGlobals(); 330 /* Pointer to library globals */ 331 332 if ((http = cg->http) == NULL) 333 { 334 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No active connection."), 1); 335 DEBUG_puts("1cupsGetResponse: No active connection - returning NULL."); 336 return (NULL); 337 } 338 } 339 340 if (http->state != HTTP_STATE_POST_RECV && http->state != HTTP_STATE_POST_SEND) 341 { 342 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No request sent."), 1); 343 DEBUG_puts("1cupsGetResponse: Not in POST state - returning NULL."); 344 return (NULL); 345 } 346 347 /* 348 * Check for an unfinished chunked request... 349 */ 350 351 if (http->data_encoding == HTTP_ENCODING_CHUNKED) 352 { 353 /* 354 * Send a 0-length chunk to finish off the request... 355 */ 356 357 DEBUG_puts("2cupsGetResponse: Finishing chunked POST..."); 358 359 if (httpWrite2(http, "", 0) < 0) 360 return (NULL); 361 } 362 363 /* 364 * Wait for a response from the server... 365 */ 366 367 DEBUG_printf(("2cupsGetResponse: Update loop, http->status=%d...", 368 http->status)); 369 370 do 371 { 372 status = httpUpdate(http); 373 } 374 while (status == HTTP_STATUS_CONTINUE); 375 376 DEBUG_printf(("2cupsGetResponse: status=%d", status)); 377 378 if (status == HTTP_STATUS_OK) 379 { 380 /* 381 * Get the IPP response... 382 */ 383 384 response = ippNew(); 385 386 while ((state = ippRead(http, response)) != IPP_STATE_DATA) 387 if (state == IPP_STATE_ERROR) 388 break; 389 390 if (state == IPP_STATE_ERROR) 391 { 392 /* 393 * Flush remaining data and delete the response... 394 */ 395 396 DEBUG_puts("1cupsGetResponse: IPP read error!"); 397 398 httpFlush(http); 399 400 ippDelete(response); 401 response = NULL; 402 403 http->status = status = HTTP_STATUS_ERROR; 404 http->error = EINVAL; 405 } 406 } 407 else if (status != HTTP_STATUS_ERROR) 408 { 409 /* 410 * Flush any error message... 411 */ 412 413 httpFlush(http); 414 415 /* 416 * Then handle encryption and authentication... 417 */ 418 419 if (status == HTTP_STATUS_UNAUTHORIZED) 420 { 421 /* 422 * See if we can do authentication... 423 */ 424 425 DEBUG_puts("2cupsGetResponse: Need authorization..."); 426 427 if (!cupsDoAuthentication(http, "POST", resource)) 428 httpReconnect2(http, 30000, NULL); 429 else 430 http->status = status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED; 431 } 432 433 #ifdef HAVE_SSL 434 else if (status == HTTP_STATUS_UPGRADE_REQUIRED) 435 { 436 /* 437 * Force a reconnect with encryption... 438 */ 439 440 DEBUG_puts("2cupsGetResponse: Need encryption..."); 441 442 if (!httpReconnect2(http, 30000, NULL)) 443 httpEncryption(http, HTTP_ENCRYPTION_REQUIRED); 444 } 445 #endif /* HAVE_SSL */ 446 } 447 448 if (response) 449 { 450 ipp_attribute_t *attr; /* status-message attribute */ 451 452 453 attr = ippFindAttribute(response, "status-message", IPP_TAG_TEXT); 454 455 DEBUG_printf(("1cupsGetResponse: status-code=%s, status-message=\"%s\"", 456 ippErrorString(response->request.status.status_code), 457 attr ? attr->values[0].string.text : "")); 458 459 _cupsSetError(response->request.status.status_code, 460 attr ? attr->values[0].string.text : 461 ippErrorString(response->request.status.status_code), 0); 462 } 463 464 return (response); 465 } 466 467 468 /* 469 * 'cupsLastError()' - Return the last IPP status code received on the current 470 * thread. 471 */ 472 473 ipp_status_t /* O - IPP status code from last request */ 474 cupsLastError(void) 475 { 476 return (_cupsGlobals()->last_error); 477 } 478 479 480 /* 481 * 'cupsLastErrorString()' - Return the last IPP status-message received on the 482 * current thread. 483 * 484 * @since CUPS 1.2/macOS 10.5@ 485 */ 486 487 const char * /* O - status-message text from last request */ 488 cupsLastErrorString(void) 489 { 490 return (_cupsGlobals()->last_status_message); 491 } 492 493 494 /* 495 * '_cupsNextDelay()' - Return the next retry delay value. 496 * 497 * This function currently returns the Fibonacci sequence 1 1 2 3 5 8. 498 * 499 * Pass 0 for the current delay value to initialize the sequence. 500 */ 501 502 int /* O - Next delay value */ 503 _cupsNextDelay(int current, /* I - Current delay value or 0 */ 504 int *previous) /* IO - Previous delay value */ 505 { 506 int next; /* Next delay value */ 507 508 509 if (current > 0) 510 { 511 next = (current + *previous) % 12; 512 *previous = next < current ? 0 : current; 513 } 514 else 515 { 516 next = 1; 517 *previous = 0; 518 } 519 520 return (next); 521 } 522 523 524 /* 525 * 'cupsReadResponseData()' - Read additional data after the IPP response. 526 * 527 * This function is used after @link cupsGetResponse@ to read the PPD or document 528 * files from @code CUPS_GET_PPD@ and @code CUPS_GET_DOCUMENT@ requests, 529 * respectively. 530 * 531 * @since CUPS 1.4/macOS 10.6@ 532 */ 533 534 ssize_t /* O - Bytes read, 0 on EOF, -1 on error */ 535 cupsReadResponseData( 536 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 537 char *buffer, /* I - Buffer to use */ 538 size_t length) /* I - Number of bytes to read */ 539 { 540 /* 541 * Get the default connection as needed... 542 */ 543 544 DEBUG_printf(("cupsReadResponseData(http=%p, buffer=%p, length=" CUPS_LLFMT ")", (void *)http, (void *)buffer, CUPS_LLCAST length)); 545 546 if (!http) 547 { 548 _cups_globals_t *cg = _cupsGlobals(); 549 /* Pointer to library globals */ 550 551 if ((http = cg->http) == NULL) 552 { 553 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No active connection"), 1); 554 return (-1); 555 } 556 } 557 558 /* 559 * Then read from the HTTP connection... 560 */ 561 562 return (httpRead2(http, buffer, length)); 563 } 564 565 566 /* 567 * 'cupsSendRequest()' - Send an IPP request. 568 * 569 * Use @link cupsWriteRequestData@ to write any additional data (document, PPD 570 * file, etc.) for the request, @link cupsGetResponse@ to get the IPP response, 571 * and @link cupsReadResponseData@ to read any additional data following the 572 * response. Only one request can be sent/queued at a time per @code http_t@ 573 * connection. 574 * 575 * Returns the initial HTTP status code, which will be @code HTTP_STATUS_CONTINUE@ 576 * on a successful send of the request. 577 * 578 * Note: Unlike @link cupsDoFileRequest@, @link cupsDoIORequest@, and 579 * @link cupsDoRequest@, the request is NOT freed with @link ippDelete@. 580 * 581 * @since CUPS 1.4/macOS 10.6@ 582 */ 583 584 http_status_t /* O - Initial HTTP status */ 585 cupsSendRequest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 586 ipp_t *request, /* I - IPP request */ 587 const char *resource, /* I - Resource path */ 588 size_t length) /* I - Length of data to follow or @code CUPS_LENGTH_VARIABLE@ */ 589 { 590 http_status_t status; /* Status of HTTP request */ 591 int got_status; /* Did we get the status? */ 592 ipp_state_t state; /* State of IPP processing */ 593 http_status_t expect; /* Expect: header to use */ 594 char date[256]; /* Date: header value */ 595 596 597 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)); 598 599 /* 600 * Range check input... 601 */ 602 603 if (!request || !resource) 604 { 605 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 606 607 return (HTTP_STATUS_ERROR); 608 } 609 610 /* 611 * Get the default connection as needed... 612 */ 613 614 if (!http && (http = _cupsConnect()) == NULL) 615 return (HTTP_STATUS_SERVICE_UNAVAILABLE); 616 617 /* 618 * If the prior request was not flushed out, do so now... 619 */ 620 621 if (http->state == HTTP_STATE_GET_SEND || 622 http->state == HTTP_STATE_POST_SEND) 623 { 624 DEBUG_puts("2cupsSendRequest: Flush prior response."); 625 httpFlush(http); 626 } 627 else if (http->state != HTTP_STATE_WAITING) 628 { 629 DEBUG_printf(("1cupsSendRequest: Unknown HTTP state (%d), " 630 "reconnecting.", http->state)); 631 if (httpReconnect2(http, 30000, NULL)) 632 return (HTTP_STATUS_ERROR); 633 } 634 635 #ifdef HAVE_SSL 636 /* 637 * See if we have an auth-info attribute and are communicating over 638 * a non-local link. If so, encrypt the link so that we can pass 639 * the authentication information securely... 640 */ 641 642 if (ippFindAttribute(request, "auth-info", IPP_TAG_TEXT) && 643 !httpAddrLocalhost(http->hostaddr) && !http->tls && 644 httpEncryption(http, HTTP_ENCRYPTION_REQUIRED)) 645 { 646 DEBUG_puts("1cupsSendRequest: Unable to encrypt connection."); 647 return (HTTP_STATUS_SERVICE_UNAVAILABLE); 648 } 649 #endif /* HAVE_SSL */ 650 651 /* 652 * Reconnect if the last response had a "Connection: close"... 653 */ 654 655 if (!_cups_strcasecmp(http->fields[HTTP_FIELD_CONNECTION], "close")) 656 { 657 DEBUG_puts("2cupsSendRequest: Connection: close"); 658 httpClearFields(http); 659 if (httpReconnect2(http, 30000, NULL)) 660 { 661 DEBUG_puts("1cupsSendRequest: Unable to reconnect."); 662 return (HTTP_STATUS_SERVICE_UNAVAILABLE); 663 } 664 } 665 666 /* 667 * Loop until we can send the request without authorization problems. 668 */ 669 670 expect = HTTP_STATUS_CONTINUE; 671 672 for (;;) 673 { 674 DEBUG_puts("2cupsSendRequest: Setup..."); 675 676 /* 677 * Setup the HTTP variables needed... 678 */ 679 680 httpClearFields(http); 681 httpSetExpect(http, expect); 682 httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp"); 683 httpSetField(http, HTTP_FIELD_DATE, httpGetDateString2(time(NULL), date, (int)sizeof(date))); 684 httpSetLength(http, length); 685 686 #ifdef HAVE_GSSAPI 687 if (http->authstring && !strncmp(http->authstring, "Negotiate", 9)) 688 { 689 /* 690 * Do not use cached Kerberos credentials since they will look like a 691 * "replay" attack... 692 */ 693 694 _cupsSetNegotiateAuthString(http, "POST", resource); 695 } 696 #endif /* HAVE_GSSAPI */ 697 698 httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring); 699 700 DEBUG_printf(("2cupsSendRequest: authstring=\"%s\"", http->authstring)); 701 702 /* 703 * Try the request... 704 */ 705 706 DEBUG_puts("2cupsSendRequest: Sending HTTP POST..."); 707 708 if (httpPost(http, resource)) 709 { 710 DEBUG_puts("2cupsSendRequest: POST failed, reconnecting."); 711 if (httpReconnect2(http, 30000, NULL)) 712 { 713 DEBUG_puts("1cupsSendRequest: Unable to reconnect."); 714 return (HTTP_STATUS_SERVICE_UNAVAILABLE); 715 } 716 else 717 continue; 718 } 719 720 /* 721 * Send the IPP data... 722 */ 723 724 DEBUG_puts("2cupsSendRequest: Writing IPP request..."); 725 726 request->state = IPP_STATE_IDLE; 727 status = HTTP_STATUS_CONTINUE; 728 got_status = 0; 729 730 while ((state = ippWrite(http, request)) != IPP_STATE_DATA) 731 { 732 if (httpCheck(http)) 733 { 734 got_status = 1; 735 736 _httpUpdate(http, &status); 737 if (status >= HTTP_STATUS_MULTIPLE_CHOICES) 738 break; 739 } 740 else if (state == IPP_STATE_ERROR) 741 break; 742 } 743 744 if (state == IPP_STATE_ERROR) 745 { 746 /* 747 * We weren't able to send the IPP request. But did we already get a HTTP 748 * error status? 749 */ 750 751 if (!got_status || status < HTTP_STATUS_MULTIPLE_CHOICES) 752 { 753 /* 754 * No, something else went wrong. 755 */ 756 757 DEBUG_puts("1cupsSendRequest: Unable to send IPP request."); 758 759 http->status = HTTP_STATUS_ERROR; 760 http->state = HTTP_STATE_WAITING; 761 762 return (HTTP_STATUS_ERROR); 763 } 764 } 765 766 /* 767 * Wait up to 1 second to get the 100-continue response as needed... 768 */ 769 770 if (!got_status) 771 { 772 if (expect == HTTP_STATUS_CONTINUE) 773 { 774 DEBUG_puts("2cupsSendRequest: Waiting for 100-continue..."); 775 776 if (httpWait(http, 1000)) 777 _httpUpdate(http, &status); 778 } 779 else if (httpCheck(http)) 780 _httpUpdate(http, &status); 781 } 782 783 DEBUG_printf(("2cupsSendRequest: status=%d", status)); 784 785 /* 786 * Process the current HTTP status... 787 */ 788 789 if (status >= HTTP_STATUS_MULTIPLE_CHOICES) 790 { 791 int temp_status; /* Temporary status */ 792 793 _cupsSetHTTPError(status); 794 795 do 796 { 797 temp_status = httpUpdate(http); 798 } 799 while (temp_status != HTTP_STATUS_ERROR && 800 http->state == HTTP_STATE_POST_RECV); 801 802 httpFlush(http); 803 } 804 805 switch (status) 806 { 807 case HTTP_STATUS_CONTINUE : 808 case HTTP_STATUS_OK : 809 case HTTP_STATUS_ERROR : 810 DEBUG_printf(("1cupsSendRequest: Returning %d.", status)); 811 return (status); 812 813 case HTTP_STATUS_UNAUTHORIZED : 814 if (cupsDoAuthentication(http, "POST", resource)) 815 { 816 DEBUG_puts("1cupsSendRequest: Returning HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED."); 817 return (HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED); 818 } 819 820 DEBUG_puts("2cupsSendRequest: Reconnecting after HTTP_STATUS_UNAUTHORIZED."); 821 822 if (httpReconnect2(http, 30000, NULL)) 823 { 824 DEBUG_puts("1cupsSendRequest: Unable to reconnect."); 825 return (HTTP_STATUS_SERVICE_UNAVAILABLE); 826 } 827 break; 828 829 #ifdef HAVE_SSL 830 case HTTP_STATUS_UPGRADE_REQUIRED : 831 /* 832 * Flush any error message, reconnect, and then upgrade with 833 * encryption... 834 */ 835 836 DEBUG_puts("2cupsSendRequest: Reconnecting after " 837 "HTTP_STATUS_UPGRADE_REQUIRED."); 838 839 if (httpReconnect2(http, 30000, NULL)) 840 { 841 DEBUG_puts("1cupsSendRequest: Unable to reconnect."); 842 return (HTTP_STATUS_SERVICE_UNAVAILABLE); 843 } 844 845 DEBUG_puts("2cupsSendRequest: Upgrading to TLS."); 846 if (httpEncryption(http, HTTP_ENCRYPTION_REQUIRED)) 847 { 848 DEBUG_puts("1cupsSendRequest: Unable to encrypt connection."); 849 return (HTTP_STATUS_SERVICE_UNAVAILABLE); 850 } 851 break; 852 #endif /* HAVE_SSL */ 853 854 case HTTP_STATUS_EXPECTATION_FAILED : 855 /* 856 * Don't try using the Expect: header the next time around... 857 */ 858 859 expect = (http_status_t)0; 860 861 DEBUG_puts("2cupsSendRequest: Reconnecting after " 862 "HTTP_EXPECTATION_FAILED."); 863 864 if (httpReconnect2(http, 30000, NULL)) 865 { 866 DEBUG_puts("1cupsSendRequest: Unable to reconnect."); 867 return (HTTP_STATUS_SERVICE_UNAVAILABLE); 868 } 869 break; 870 871 default : 872 /* 873 * Some other error... 874 */ 875 876 return (status); 877 } 878 } 879 } 880 881 882 /* 883 * 'cupsWriteRequestData()' - Write additional data after an IPP request. 884 * 885 * This function is used after @link cupsSendRequest@ to provide a PPD and 886 * after @link cupsStartDocument@ to provide a document file. 887 * 888 * @since CUPS 1.4/macOS 10.6@ 889 */ 890 891 http_status_t /* O - @code HTTP_STATUS_CONTINUE@ if OK or HTTP status on error */ 892 cupsWriteRequestData( 893 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ 894 const char *buffer, /* I - Bytes to write */ 895 size_t length) /* I - Number of bytes to write */ 896 { 897 int wused; /* Previous bytes in buffer */ 898 899 900 /* 901 * Get the default connection as needed... 902 */ 903 904 DEBUG_printf(("cupsWriteRequestData(http=%p, buffer=%p, length=" CUPS_LLFMT ")", (void *)http, (void *)buffer, CUPS_LLCAST length)); 905 906 if (!http) 907 { 908 _cups_globals_t *cg = _cupsGlobals(); 909 /* Pointer to library globals */ 910 911 if ((http = cg->http) == NULL) 912 { 913 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No active connection"), 1); 914 DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_ERROR."); 915 return (HTTP_STATUS_ERROR); 916 } 917 } 918 919 /* 920 * Then write to the HTTP connection... 921 */ 922 923 wused = http->wused; 924 925 if (httpWrite2(http, buffer, length) < 0) 926 { 927 DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_ERROR."); 928 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(http->error), 0); 929 return (HTTP_STATUS_ERROR); 930 } 931 932 /* 933 * Finally, check if we have any pending data from the server... 934 */ 935 936 if (length >= HTTP_MAX_BUFFER || 937 http->wused < wused || 938 (wused > 0 && (size_t)http->wused == length)) 939 { 940 /* 941 * We've written something to the server, so check for response data... 942 */ 943 944 if (_httpWait(http, 0, 1)) 945 { 946 http_status_t status; /* Status from _httpUpdate */ 947 948 _httpUpdate(http, &status); 949 if (status >= HTTP_STATUS_MULTIPLE_CHOICES) 950 { 951 _cupsSetHTTPError(status); 952 953 do 954 { 955 status = httpUpdate(http); 956 } 957 while (status != HTTP_STATUS_ERROR && http->state == HTTP_STATE_POST_RECV); 958 959 httpFlush(http); 960 } 961 962 DEBUG_printf(("1cupsWriteRequestData: Returning %d.\n", status)); 963 return (status); 964 } 965 } 966 967 DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_CONTINUE."); 968 return (HTTP_STATUS_CONTINUE); 969 } 970 971 972 /* 973 * '_cupsConnect()' - Get the default server connection... 974 */ 975 976 http_t * /* O - HTTP connection */ 977 _cupsConnect(void) 978 { 979 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */ 980 981 982 /* 983 * See if we are connected to the same server... 984 */ 985 986 if (cg->http) 987 { 988 /* 989 * Compare the connection hostname, port, and encryption settings to 990 * the cached defaults; these were initialized the first time we 991 * connected... 992 */ 993 994 if (strcmp(cg->http->hostname, cg->server) || 995 #ifdef AF_LOCAL 996 (httpAddrFamily(cg->http->hostaddr) != AF_LOCAL && cg->ipp_port != httpAddrPort(cg->http->hostaddr)) || 997 #else 998 cg->ipp_port != httpAddrPort(cg->http->hostaddr) || 999 #endif /* AF_LOCAL */ 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