Home | History | Annotate | Download | only in cups
      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