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