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