1 /*
2  * "$Id: request.c 9322 2010-10-01 22:40:38Z mike $"
3  *
4  *   IPP utilities for the Common UNIX Printing System (CUPS).
5  *
6  *   Copyright 2007-2010 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  *   cupsReadResponseData() - Read additional data after the IPP response.
24  *   cupsSendRequest()      - Send an IPP request.
25  *   cupsWriteRequestData() - Write additional data after an IPP request.
26  *   _cupsSetError()        - Set the last IPP status code and status-message.
27  *   _cupsSetHTTPError()    - Set the last error using the HTTP status.
28  */
29 
30 /*
31  * Include necessary headers...
32  */
33 
34 #include "globals.h"
35 #include "debug.h"
36 #include <stdlib.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <sys/stat.h>
40 #if defined(WIN32) || defined(__EMX__)
41 #  include <io.h>
42 #else
43 #  include <unistd.h>
44 #endif /* WIN32 || __EMX__ */
45 #ifndef O_BINARY
46 #  define O_BINARY 0
47 #endif /* O_BINARY */
48 
49 
50 /*
51  * 'cupsDoFileRequest()' - Do an IPP request with a file.
52  *
53  * This function sends the IPP request to the specified server, retrying
54  * and authenticating as necessary.  The request is freed with @link ippDelete@
55  * after receiving a valid IPP response.
56  */
57 
58 ipp_t *					/* O - Response data */
cupsDoFileRequest(http_t * http,ipp_t * request,const char * resource,const char * filename)59 cupsDoFileRequest(http_t     *http,	/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
60                   ipp_t      *request,	/* I - IPP request */
61                   const char *resource,	/* I - HTTP resource for POST */
62 		  const char *filename)	/* I - File to send or @code NULL@ for none */
63 {
64   ipp_t		*response;		/* IPP response data */
65   int		infile;			/* Input file */
66 
67 
68   DEBUG_printf(("cupsDoFileRequest(http=%p, request=%p(%s), resource=\"%s\", "
69                 "filename=\"%s\")", http, request,
70 		request ? ippOpString(request->request.op.operation_id) : "?",
71 		resource, filename));
72 
73   if (filename)
74   {
75     if ((infile = open(filename, O_RDONLY | O_BINARY)) < 0)
76     {
77      /*
78       * Can't get file information!
79       */
80 
81       _cupsSetError(errno == ENOENT ? IPP_NOT_FOUND : IPP_NOT_AUTHORIZED,
82                     NULL, 0);
83 
84       ippDelete(request);
85 
86       return (NULL);
87     }
88   }
89   else
90     infile = -1;
91 
92   response = cupsDoIORequest(http, request, resource, infile, -1);
93 
94   if (infile >= 0)
95     close(infile);
96 
97   return (response);
98 }
99 
100 
101 /*
102  * 'cupsDoIORequest()' - Do an IPP request with file descriptors.
103  *
104  * This function sends the IPP request to the specified server, retrying
105  * and authenticating as necessary.  The request is freed with ippDelete()
106  * after receiving a valid IPP response.
107  *
108  * If "infile" is a valid file descriptor, cupsDoIORequest() copies
109  * all of the data from the file after the IPP request message.
110  *
111  * If "outfile" is a valid file descriptor, cupsDoIORequest() copies
112  * all of the data after the IPP response message to the file.
113  *
114  * @since CUPS 1.3/Mac OS X 10.5@
115  */
116 
117 ipp_t *					/* O - Response data */
cupsDoIORequest(http_t * http,ipp_t * request,const char * resource,int infile,int outfile)118 cupsDoIORequest(http_t     *http,	/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
119                 ipp_t      *request,	/* I - IPP request */
120                 const char *resource,	/* I - HTTP resource for POST */
121 		int        infile,	/* I - File to read from or -1 for none */
122 		int        outfile)	/* I - File to write to or -1 for none */
123 {
124   ipp_t		*response = NULL;	/* IPP response data */
125   size_t	length = 0;		/* Content-Length value */
126   http_status_t	status;			/* Status of HTTP request */
127   struct stat	fileinfo;		/* File information */
128   int		bytes;			/* Number of bytes read/written */
129   char		buffer[32768];		/* Output buffer */
130 
131 
132   DEBUG_printf(("cupsDoIORequest(http=%p, request=%p(%s), resource=\"%s\", "
133                 "infile=%d, outfile=%d)", http, request,
134 		request ? ippOpString(request->request.op.operation_id) : "?",
135 		resource, infile, outfile));
136 
137  /*
138   * Range check input...
139   */
140 
141   if (!request || !resource)
142   {
143     ippDelete(request);
144 
145     _cupsSetError(IPP_INTERNAL_ERROR, strerror(EINVAL), 0);
146 
147     return (NULL);
148   }
149 
150  /*
151   * Get the default connection as needed...
152   */
153 
154   if (!http)
155     if ((http = _cupsConnect()) == NULL)
156       return (NULL);
157 
158  /*
159   * See if we have a file to send...
160   */
161 
162   if (infile >= 0)
163   {
164     if (fstat(infile, &fileinfo))
165     {
166      /*
167       * Can't get file information!
168       */
169 
170       _cupsSetError(errno == EBADF ? IPP_NOT_FOUND : IPP_NOT_AUTHORIZED,
171                     NULL, 0);
172 
173       ippDelete(request);
174 
175       return (NULL);
176     }
177 
178 #ifdef WIN32
179     if (fileinfo.st_mode & _S_IFDIR)
180 #else
181     if (S_ISDIR(fileinfo.st_mode))
182 #endif /* WIN32 */
183     {
184      /*
185       * Can't send a directory...
186       */
187 
188       ippDelete(request);
189 
190       _cupsSetError(IPP_NOT_POSSIBLE, strerror(EISDIR), 0);
191 
192       return (NULL);
193     }
194 
195 #ifndef WIN32
196     if (!S_ISREG(fileinfo.st_mode))
197       length = 0;			/* Chunk when piping */
198     else
199 #endif /* !WIN32 */
200     length = ippLength(request) + fileinfo.st_size;
201   }
202   else
203     length = ippLength(request);
204 
205   DEBUG_printf(("2cupsDoIORequest: Request length=%ld, total length=%ld",
206                 (long)ippLength(request), (long)length));
207 
208  /*
209   * Clear any "Local" authentication data since it is probably stale...
210   */
211 
212   if (http->authstring && !strncmp(http->authstring, "Local ", 6))
213     httpSetAuthString(http, NULL, NULL);
214 
215  /*
216   * Loop until we can send the request without authorization problems.
217   */
218 
219   while (response == NULL)
220   {
221     DEBUG_puts("2cupsDoIORequest: setup...");
222 
223    /*
224     * Send the request...
225     */
226 
227     status = cupsSendRequest(http, request, resource, length);
228 
229     DEBUG_printf(("2cupsDoIORequest: status=%d", status));
230 
231     if (status == HTTP_CONTINUE && request->state == IPP_DATA && infile >= 0)
232     {
233       DEBUG_puts("2cupsDoIORequest: file write...");
234 
235      /*
236       * Send the file with the request...
237       */
238 
239 #ifndef WIN32
240       if (S_ISREG(fileinfo.st_mode))
241 #endif /* WIN32 */
242       lseek(infile, 0, SEEK_SET);
243 
244       while ((bytes = (int)read(infile, buffer, sizeof(buffer))) > 0)
245       {
246 	if (httpCheck(http))
247 	{
248 	  if ((status = httpUpdate(http)) != HTTP_CONTINUE)
249 	    break;
250         }
251 
252   	if (httpWrite2(http, buffer, bytes) < bytes)
253           break;
254       }
255     }
256 
257    /*
258     * Get the server's response...
259     */
260 
261     if (status == HTTP_CONTINUE || status == HTTP_OK)
262     {
263       response = cupsGetResponse(http, resource);
264       status   = http->status;
265     }
266     else
267       httpFlush(http);
268 
269     DEBUG_printf(("2cupsDoIORequest: status=%d", status));
270 
271     if (status == HTTP_ERROR ||
272         (status >= HTTP_BAD_REQUEST && status != HTTP_UNAUTHORIZED &&
273 	 status != HTTP_UPGRADE_REQUIRED))
274     {
275       _cupsSetHTTPError(status);
276       break;
277     }
278 
279     if (response)
280     {
281       if (outfile >= 0)
282       {
283        /*
284         * Write trailing data to file...
285 	*/
286 
287 	while ((bytes = (int)httpRead2(http, buffer, sizeof(buffer))) > 0)
288 	  if (write(outfile, buffer, bytes) < bytes)
289 	    break;
290       }
291       else
292       {
293        /*
294         * Flush any remaining data...
295         */
296 
297         httpFlush(http);
298       }
299     }
300   }
301 
302  /*
303   * Delete the original request and return the response...
304   */
305 
306   ippDelete(request);
307 
308   return (response);
309 }
310 
311 
312 /*
313  * 'cupsDoRequest()' - Do an IPP request.
314  *
315  * This function sends the IPP request to the specified server, retrying
316  * and authenticating as necessary.  The request is freed with ippDelete()
317  * after receiving a valid IPP response.
318  */
319 
320 ipp_t *					/* O - Response data */
cupsDoRequest(http_t * http,ipp_t * request,const char * resource)321 cupsDoRequest(http_t     *http,		/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
322               ipp_t      *request,	/* I - IPP request */
323               const char *resource)	/* I - HTTP resource for POST */
324 {
325   DEBUG_printf(("cupsDoRequest(http=%p, request=%p(%s), resource=\"%s\")",
326                 http, request,
327 		request ? ippOpString(request->request.op.operation_id) : "?",
328 		resource));
329 
330   return (cupsDoIORequest(http, request, resource, -1, -1));
331 }
332 
333 
334 /*
335  * 'cupsGetResponse()' - Get a response to an IPP request.
336  *
337  * Use this function to get the response for an IPP request sent using
338  * cupsSendDocument() or cupsSendRequest(). For requests that return
339  * additional data, use httpRead() after getting a successful response.
340  *
341  * @since CUPS 1.4/Mac OS X 10.6@
342  */
343 
344 ipp_t *					/* O - Response or @code NULL@ on HTTP error */
cupsGetResponse(http_t * http,const char * resource)345 cupsGetResponse(http_t     *http,	/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
346                 const char *resource)	/* I - HTTP resource for POST */
347 {
348   http_status_t	status;			/* HTTP status */
349   ipp_state_t	state;			/* IPP read state */
350   ipp_t		*response = NULL;	/* IPP response */
351 
352 
353   DEBUG_printf(("cupsGetResponse(http=%p, resource=\"%s\")", http, resource));
354 
355  /*
356   * Connect to the default server as needed...
357   */
358 
359   if (!http)
360     http = _cupsConnect();
361 
362   if (!http || (http->state != HTTP_POST_RECV && http->state != HTTP_POST_SEND))
363     return (NULL);
364 
365  /*
366   * Check for an unfinished chunked request...
367   */
368 
369   if (http->data_encoding == HTTP_ENCODE_CHUNKED)
370   {
371    /*
372     * Send a 0-length chunk to finish off the request...
373     */
374 
375     DEBUG_puts("2cupsGetResponse: Finishing chunked POST...");
376 
377     if (httpWrite2(http, "", 0) < 0)
378       return (NULL);
379   }
380 
381  /*
382   * Wait for a response from the server...
383   */
384 
385   DEBUG_printf(("2cupsGetResponse: Update loop, http->status=%d...",
386                 http->status));
387 
388   status = http->status;
389   while (status == HTTP_CONTINUE)
390     status = httpUpdate(http);
391 
392   DEBUG_printf(("2cupsGetResponse: status=%d", status));
393 
394   if (status == HTTP_OK)
395   {
396    /*
397     * Get the IPP response...
398     */
399 
400     response = ippNew();
401 
402     while ((state = ippRead(http, response)) != IPP_DATA)
403       if (state == IPP_ERROR)
404 	break;
405 
406     if (state == IPP_ERROR)
407     {
408      /*
409       * Delete the response...
410       */
411 
412       DEBUG_puts("1cupsGetResponse: IPP read error!");
413 
414       ippDelete(response);
415       response = NULL;
416 
417       _cupsSetError(IPP_SERVICE_UNAVAILABLE, NULL, 0);
418     }
419   }
420   else if (status != HTTP_ERROR)
421   {
422    /*
423     * Flush any error message...
424     */
425 
426     httpFlush(http);
427 
428    /*
429     * Then handle encryption and authentication...
430     */
431 
432     if (status == HTTP_UNAUTHORIZED)
433     {
434      /*
435       * See if we can do authentication...
436       */
437 
438       DEBUG_puts("2cupsGetResponse: Need authorization...");
439 
440       if (!cupsDoAuthentication(http, "POST", resource))
441 	httpReconnect(http);
442       else
443         status = HTTP_AUTHORIZATION_CANCELED;
444     }
445 
446 #ifdef HAVE_SSL
447     else if (status == HTTP_UPGRADE_REQUIRED)
448     {
449      /*
450       * Force a reconnect with encryption...
451       */
452 
453       DEBUG_puts("2cupsGetResponse: Need encryption...");
454 
455       if (!httpReconnect(http))
456         httpEncryption(http, HTTP_ENCRYPT_REQUIRED);
457     }
458 #endif /* HAVE_SSL */
459   }
460 
461   if (response)
462   {
463     ipp_attribute_t	*attr;		/* status-message attribute */
464 
465 
466     attr = ippFindAttribute(response, "status-message", IPP_TAG_TEXT);
467 
468     DEBUG_printf(("1cupsGetResponse: status-code=%s, status-message=\"%s\"",
469                   ippErrorString(response->request.status.status_code),
470                   attr ? attr->values[0].string.text : ""));
471 
472     _cupsSetError(response->request.status.status_code,
473                   attr ? attr->values[0].string.text :
474 		      ippErrorString(response->request.status.status_code), 0);
475   }
476   else if (status != HTTP_OK)
477     _cupsSetHTTPError(status);
478 
479   return (response);
480 }
481 
482 
483 /*
484  * 'cupsReadResponseData()' - Read additional data after the IPP response.
485  *
486  * This function is used after cupsGetResponse() to read the PPD or document
487  * files for CUPS_GET_PPD and CUPS_GET_DOCUMENT requests, respectively.
488  *
489  * @since CUPS 1.4/Mac OS X 10.6@
490  */
491 
492 ssize_t					/* O - Bytes read, 0 on EOF, -1 on error */
cupsReadResponseData(http_t * http,char * buffer,size_t length)493 cupsReadResponseData(
494     http_t *http,			/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
495     char   *buffer,			/* I - Buffer to use */
496     size_t length)			/* I - Number of bytes to read */
497 {
498  /*
499   * Get the default connection as needed...
500   */
501 
502   DEBUG_printf(("cupsReadResponseData(http=%p, buffer=%p, "
503                 "length=" CUPS_LLFMT ")", http, buffer, CUPS_LLCAST length));
504 
505   if (!http)
506   {
507     _cups_globals_t *cg = _cupsGlobals();
508 					/* Pointer to library globals */
509 
510     if ((http = cg->http) == NULL)
511     {
512       _cupsSetError(IPP_INTERNAL_ERROR, _("No active connection"), 1);
513       return (-1);
514     }
515   }
516 
517  /*
518   * Then read from the HTTP connection...
519   */
520 
521   return (httpRead2(http, buffer, length));
522 }
523 
524 
525 /*
526  * 'cupsSendRequest()' - Send an IPP request.
527  *
528  * Use httpWrite() to write any additional data (document, PPD file, etc.)
529  * for the request, cupsGetResponse() to get the IPP response, and httpRead()
530  * to read any additional data following the response. Only one request can be
531  * sent/queued at a time.
532  *
533  * Unlike cupsDoFileRequest(), cupsDoIORequest(), and cupsDoRequest(), the
534  * request is not freed.
535  *
536  * @since CUPS 1.4/Mac OS X 10.6@
537  */
538 
539 http_status_t				/* O - Initial HTTP status */
cupsSendRequest(http_t * http,ipp_t * request,const char * resource,size_t length)540 cupsSendRequest(http_t     *http,	/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
541                 ipp_t      *request,	/* I - IPP request */
542                 const char *resource,	/* I - Resource path */
543 		size_t     length)	/* I - Length of data to follow or @code CUPS_LENGTH_VARIABLE@ */
544 {
545   http_status_t	status;			/* Status of HTTP request */
546   int		got_status;		/* Did we get the status? */
547   ipp_state_t	state;			/* State of IPP processing */
548   http_status_t	expect;			/* Expect: header to use */
549 
550 
551   DEBUG_printf(("cupsSendRequest(http=%p, request=%p(%s), resource=\"%s\", "
552                 "length=" CUPS_LLFMT ")", http, request,
553 		request ? ippOpString(request->request.op.operation_id) : "?",
554 		resource, CUPS_LLCAST length));
555 
556  /*
557   * Range check input...
558   */
559 
560   if (!request || !resource)
561   {
562     _cupsSetError(IPP_INTERNAL_ERROR, strerror(EINVAL), 0);
563 
564     return (HTTP_ERROR);
565   }
566 
567  /*
568   * Get the default connection as needed...
569   */
570 
571   if (!http)
572     if ((http = _cupsConnect()) == NULL)
573       return (HTTP_SERVICE_UNAVAILABLE);
574 
575 #ifdef HAVE_SSL
576  /*
577   * See if we have an auth-info attribute and are communicating over
578   * a non-local link.  If so, encrypt the link so that we can pass
579   * the authentication information securely...
580   */
581 
582   if (ippFindAttribute(request, "auth-info", IPP_TAG_TEXT) &&
583       !httpAddrLocalhost(http->hostaddr) && !http->tls &&
584       httpEncryption(http, HTTP_ENCRYPT_REQUIRED))
585   {
586     _cupsSetError(IPP_SERVICE_UNAVAILABLE, NULL, 0);
587     return (HTTP_SERVICE_UNAVAILABLE);
588   }
589 #endif /* HAVE_SSL */
590 
591  /*
592   * Reconnect if the last response had a "Connection: close"...
593   */
594 
595   if (!strcasecmp(http->fields[HTTP_FIELD_CONNECTION], "close"))
596     if (httpReconnect(http))
597     {
598       _cupsSetError(IPP_SERVICE_UNAVAILABLE, NULL, 0);
599       return (HTTP_SERVICE_UNAVAILABLE);
600     }
601 
602  /*
603   * Loop until we can send the request without authorization problems.
604   */
605 
606   expect = HTTP_CONTINUE;
607 
608   for (;;)
609   {
610     DEBUG_puts("2cupsSendRequest: Setup...");
611 
612    /*
613     * Setup the HTTP variables needed...
614     */
615 
616     httpClearFields(http);
617     httpSetLength(http, length);
618     httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
619     httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring);
620     httpSetExpect(http, expect);
621 
622     DEBUG_printf(("2cupsSendRequest: authstring=\"%s\"", http->authstring));
623 
624    /*
625     * Try the request...
626     */
627 
628     DEBUG_puts("2cupsSendRequest: Sending HTTP POST...");
629 
630     if (httpPost(http, resource))
631     {
632       if (httpReconnect(http))
633       {
634         _cupsSetError(IPP_SERVICE_UNAVAILABLE, NULL, 0);
635         return (HTTP_SERVICE_UNAVAILABLE);
636       }
637       else
638         continue;
639     }
640 
641    /*
642     * Send the IPP data...
643     */
644 
645     DEBUG_puts("2cupsSendRequest: Writing IPP request...");
646 
647     request->state = IPP_IDLE;
648     status         = HTTP_CONTINUE;
649     got_status     = 0;
650 
651     while ((state = ippWrite(http, request)) != IPP_DATA)
652       if (state == IPP_ERROR)
653 	break;
654       else if (httpCheck(http))
655       {
656         got_status = 1;
657 
658 	if ((status = httpUpdate(http)) != HTTP_CONTINUE)
659 	  break;
660       }
661 
662    /*
663     * Wait up to 1 second to get the 100-continue response as needed...
664     */
665 
666     if (!got_status && expect == HTTP_CONTINUE)
667     {
668       DEBUG_puts("2cupsSendRequest: Waiting for 100-continue...");
669 
670       if (httpWait(http, 1000))
671         status = httpUpdate(http);
672     }
673     else if (httpCheck(http))
674       status = httpUpdate(http);
675 
676     DEBUG_printf(("2cupsSendRequest: status=%d", status));
677 
678    /*
679     * Process the current HTTP status...
680     */
681 
682     if (status >= HTTP_BAD_REQUEST)
683       httpFlush(http);
684 
685     switch (status)
686     {
687       case HTTP_ERROR :
688       case HTTP_CONTINUE :
689       case HTTP_OK :
690           return (status);
691 
692       case HTTP_UNAUTHORIZED :
693           if (cupsDoAuthentication(http, "POST", resource))
694 	    return (HTTP_AUTHORIZATION_CANCELED);
695 
696 	  if (httpReconnect(http))
697 	  {
698 	    _cupsSetError(IPP_SERVICE_UNAVAILABLE, NULL, 0);
699 	    return (HTTP_SERVICE_UNAVAILABLE);
700 	  }
701 	  break;
702 
703 #ifdef HAVE_SSL
704       case HTTP_UPGRADE_REQUIRED :
705 	 /*
706 	  * Flush any error message, reconnect, and then upgrade with
707 	  * encryption...
708 	  */
709 
710 	  if (httpReconnect(http))
711 	  {
712 	    _cupsSetError(IPP_SERVICE_UNAVAILABLE, NULL, 0);
713 	    return (HTTP_SERVICE_UNAVAILABLE);
714 	  }
715 
716 	  if (httpEncryption(http, HTTP_ENCRYPT_REQUIRED))
717 	  {
718 	    _cupsSetError(IPP_SERVICE_UNAVAILABLE, NULL, 0);
719 	    return (HTTP_SERVICE_UNAVAILABLE);
720 	  }
721 	  break;
722 #endif /* HAVE_SSL */
723 
724       case HTTP_EXPECTATION_FAILED :
725 	 /*
726 	  * Don't try using the Expect: header the next time around...
727 	  */
728 
729 	  expect = (http_status_t)0;
730 
731 	  if (httpReconnect(http))
732 	  {
733 	    _cupsSetError(IPP_SERVICE_UNAVAILABLE, NULL, 0);
734 	    return (HTTP_SERVICE_UNAVAILABLE);
735 	  }
736 	  break;
737 
738       default :
739          /*
740 	  * Some other error...
741 	  */
742 
743 	  return (status);
744     }
745   }
746 }
747 
748 
749 /*
750  * 'cupsWriteRequestData()' - Write additional data after an IPP request.
751  *
752  * This function is used after @link cupsSendRequest@ to provide a PPD and
753  * after @link cupsStartDocument@ to provide a document file.
754  *
755  * @since CUPS 1.4/Mac OS X 10.6@
756  */
757 
758 http_status_t				/* O - @code HTTP_CONTINUE@ if OK or HTTP status on error */
cupsWriteRequestData(http_t * http,const char * buffer,size_t length)759 cupsWriteRequestData(
760     http_t     *http,			/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
761     const char *buffer,			/* I - Bytes to write */
762     size_t     length)			/* I - Number of bytes to write */
763 {
764   int	wused;				/* Previous bytes in buffer */
765 
766 
767  /*
768   * Get the default connection as needed...
769   */
770 
771   DEBUG_printf(("cupsWriteRequestData(http=%p, buffer=%p, "
772                 "length=" CUPS_LLFMT ")", http, buffer, CUPS_LLCAST length));
773 
774   if (!http)
775   {
776     _cups_globals_t *cg = _cupsGlobals();
777 					/* Pointer to library globals */
778 
779     if ((http = cg->http) == NULL)
780     {
781       _cupsSetError(IPP_INTERNAL_ERROR, _("No active connection"), 1);
782       return (HTTP_ERROR);
783     }
784   }
785 
786  /*
787   * Then write to the HTTP connection...
788   */
789 
790   wused = http->wused;
791 
792   if (httpWrite2(http, buffer, length) < 0)
793     return (HTTP_ERROR);
794 
795  /*
796   * Finally, check if we have any pending data from the server...
797   */
798 
799   if (length >= HTTP_MAX_BUFFER ||
800       http->wused < wused ||
801       (wused > 0 && http->wused == length))
802   {
803    /*
804     * We've written something to the server, so check for response data...
805     */
806 
807     if (_httpWait(http, 0, 1))
808     {
809       http_status_t	status;		/* Status from httpUpdate */
810 
811       if ((status = httpUpdate(http)) >= HTTP_BAD_REQUEST)
812         httpFlush(http);
813 
814       return (status);
815     }
816   }
817 
818   return (HTTP_CONTINUE);
819 }
820 
821 
822 /*
823  * '_cupsSetError()' - Set the last IPP status code and status-message.
824  */
825 
826 void
_cupsSetError(ipp_status_t status,const char * message,int localize)827 _cupsSetError(ipp_status_t status,	/* I - IPP status code */
828               const char   *message,	/* I - status-message value */
829 	      int          localize)	/* I - Localize the message? */
830 {
831   _cups_globals_t	*cg;		/* Global data */
832 
833 
834   if (!message && errno)
835   {
836     message  = strerror(errno);
837     localize = 0;
838   }
839 
840   cg             = _cupsGlobals();
841   cg->last_error = status;
842 
843   if (cg->last_status_message)
844   {
845     _cupsStrFree(cg->last_status_message);
846 
847     cg->last_status_message = NULL;
848   }
849 
850   if (message)
851   {
852     if (localize)
853     {
854      /*
855       * Get the message catalog...
856       */
857 
858       if (!cg->lang_default)
859 	cg->lang_default = cupsLangDefault();
860 
861       cg->last_status_message = _cupsStrAlloc(_cupsLangString(cg->lang_default,
862                                                               message));
863     }
864     else
865       cg->last_status_message = _cupsStrAlloc(message);
866   }
867 
868   DEBUG_printf(("4_cupsSetError: last_error=%s, last_status_message=\"%s\"",
869                 ippErrorString(cg->last_error), cg->last_status_message));
870 }
871 
872 
873 /*
874  * '_cupsSetHTTPError()' - Set the last error using the HTTP status.
875  */
876 
877 void
_cupsSetHTTPError(http_status_t status)878 _cupsSetHTTPError(http_status_t status)	/* I - HTTP status code */
879 {
880   switch (status)
881   {
882     case HTTP_NOT_FOUND :
883 	_cupsSetError(IPP_NOT_FOUND, httpStatus(status), 0);
884 	break;
885 
886     case HTTP_UNAUTHORIZED :
887 	_cupsSetError(IPP_NOT_AUTHENTICATED, httpStatus(status), 0);
888 	break;
889 
890     case HTTP_AUTHORIZATION_CANCELED :
891     case HTTP_FORBIDDEN :
892 	_cupsSetError(IPP_FORBIDDEN, httpStatus(status), 0);
893 	break;
894 
895     case HTTP_BAD_REQUEST :
896 	_cupsSetError(IPP_BAD_REQUEST, httpStatus(status), 0);
897 	break;
898 
899     case HTTP_REQUEST_TOO_LARGE :
900 	_cupsSetError(IPP_REQUEST_VALUE, httpStatus(status), 0);
901 	break;
902 
903     case HTTP_NOT_IMPLEMENTED :
904 	_cupsSetError(IPP_OPERATION_NOT_SUPPORTED, httpStatus(status), 0);
905 	break;
906 
907     case HTTP_NOT_SUPPORTED :
908 	_cupsSetError(IPP_VERSION_NOT_SUPPORTED, httpStatus(status), 0);
909 	break;
910 
911     default :
912 	DEBUG_printf(("4_cupsSetHTTPError: HTTP error %d mapped to "
913 	              "IPP_SERVICE_UNAVAILABLE!", status));
914 	_cupsSetError(IPP_SERVICE_UNAVAILABLE, httpStatus(status), 0);
915 	break;
916   }
917 }
918 
919 
920 /*
921  * End of "$Id: request.c 9322 2010-10-01 22:40:38Z mike $".
922  */
923