1/*
2 * "$Id: request.c 11737 2014-03-26 20:48:24Z 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
62ipp_t *					/* O - Response data */
63cupsDoFileRequest(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
121ipp_t *					/* O - Response data */
122cupsDoIORequest(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
318ipp_t *					/* O - Response data */
319cupsDoRequest(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
343ipp_t *					/* O - Response or @code NULL@ on HTTP error */
344cupsGetResponse(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
354 /*
355  * Connect to the default server as needed...
356  */
357
358  if (!http)
359    http = _cupsConnect();
360
361  if (!http || (http->state != HTTP_STATE_POST_RECV &&
362                http->state != HTTP_STATE_POST_SEND))
363    return (NULL);
364
365 /*
366  * Check for an unfinished chunked request...
367  */
368
369  if (http->data_encoding == HTTP_ENCODING_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  do
389  {
390    status = httpUpdate(http);
391  }
392  while (status == HTTP_STATUS_CONTINUE);
393
394  DEBUG_printf(("2cupsGetResponse: status=%d", status));
395
396  if (status == HTTP_STATUS_OK)
397  {
398   /*
399    * Get the IPP response...
400    */
401
402    response = ippNew();
403
404    while ((state = ippRead(http, response)) != IPP_STATE_DATA)
405      if (state == IPP_STATE_ERROR)
406	break;
407
408    if (state == IPP_STATE_ERROR)
409    {
410     /*
411      * Flush remaining data and delete the response...
412      */
413
414      DEBUG_puts("1cupsGetResponse: IPP read error!");
415
416      httpFlush(http);
417
418      ippDelete(response);
419      response = NULL;
420
421      http->status = status = HTTP_STATUS_ERROR;
422      http->error  = EINVAL;
423    }
424  }
425  else if (status != HTTP_STATUS_ERROR)
426  {
427   /*
428    * Flush any error message...
429    */
430
431    httpFlush(http);
432
433   /*
434    * Then handle encryption and authentication...
435    */
436
437    if (status == HTTP_STATUS_UNAUTHORIZED)
438    {
439     /*
440      * See if we can do authentication...
441      */
442
443      DEBUG_puts("2cupsGetResponse: Need authorization...");
444
445      if (!cupsDoAuthentication(http, "POST", resource))
446        httpReconnect2(http, 30000, NULL);
447      else
448        http->status = status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
449    }
450
451#ifdef HAVE_SSL
452    else if (status == HTTP_STATUS_UPGRADE_REQUIRED)
453    {
454     /*
455      * Force a reconnect with encryption...
456      */
457
458      DEBUG_puts("2cupsGetResponse: Need encryption...");
459
460      if (!httpReconnect2(http, 30000, NULL))
461        httpEncryption(http, HTTP_ENCRYPTION_REQUIRED);
462    }
463#endif /* HAVE_SSL */
464  }
465
466  if (response)
467  {
468    ipp_attribute_t	*attr;		/* status-message attribute */
469
470
471    attr = ippFindAttribute(response, "status-message", IPP_TAG_TEXT);
472
473    DEBUG_printf(("1cupsGetResponse: status-code=%s, status-message=\"%s\"",
474                  ippErrorString(response->request.status.status_code),
475                  attr ? attr->values[0].string.text : ""));
476
477    _cupsSetError(response->request.status.status_code,
478                  attr ? attr->values[0].string.text :
479		      ippErrorString(response->request.status.status_code), 0);
480  }
481
482  return (response);
483}
484
485
486/*
487 * 'cupsLastError()' - Return the last IPP status code received on the current
488 *                     thread.
489 */
490
491ipp_status_t				/* O - IPP status code from last request */
492cupsLastError(void)
493{
494  return (_cupsGlobals()->last_error);
495}
496
497
498/*
499 * 'cupsLastErrorString()' - Return the last IPP status-message received on the
500 *                           current thread.
501 *
502 * @since CUPS 1.2/OS X 10.5@
503 */
504
505const char *				/* O - status-message text from last request */
506cupsLastErrorString(void)
507{
508  return (_cupsGlobals()->last_status_message);
509}
510
511
512/*
513 * '_cupsNextDelay()' - Return the next retry delay value.
514 *
515 * This function currently returns the Fibonacci sequence 1 1 2 3 5 8.
516 *
517 * Pass 0 for the current delay value to initialize the sequence.
518 */
519
520int					/* O  - Next delay value */
521_cupsNextDelay(int current,		/* I  - Current delay value or 0 */
522               int *previous)		/* IO - Previous delay value */
523{
524  int	next;				/* Next delay value */
525
526
527  if (current > 0)
528  {
529    next      = (current + *previous) % 12;
530    *previous = next < current ? 0 : current;
531  }
532  else
533  {
534    next      = 1;
535    *previous = 0;
536  }
537
538  return (next);
539}
540
541
542/*
543 * 'cupsReadResponseData()' - Read additional data after the IPP response.
544 *
545 * This function is used after @link cupsGetResponse@ to read the PPD or document
546 * files from @code CUPS_GET_PPD@ and @code CUPS_GET_DOCUMENT@ requests,
547 * respectively.
548 *
549 * @since CUPS 1.4/OS X 10.6@
550 */
551
552ssize_t					/* O - Bytes read, 0 on EOF, -1 on error */
553cupsReadResponseData(
554    http_t *http,			/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
555    char   *buffer,			/* I - Buffer to use */
556    size_t length)			/* I - Number of bytes to read */
557{
558 /*
559  * Get the default connection as needed...
560  */
561
562  DEBUG_printf(("cupsReadResponseData(http=%p, buffer=%p, "
563                "length=" CUPS_LLFMT ")", http, buffer, CUPS_LLCAST length));
564
565  if (!http)
566  {
567    _cups_globals_t *cg = _cupsGlobals();
568					/* Pointer to library globals */
569
570    if ((http = cg->http) == NULL)
571    {
572      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No active connection"), 1);
573      return (-1);
574    }
575  }
576
577 /*
578  * Then read from the HTTP connection...
579  */
580
581  return (httpRead2(http, buffer, length));
582}
583
584
585/*
586 * 'cupsSendRequest()' - Send an IPP request.
587 *
588 * Use @link cupsWriteRequestData@ to write any additional data (document, PPD
589 * file, etc.) for the request, @link cupsGetResponse@ to get the IPP response,
590 * and @link cupsReadResponseData@ to read any additional data following the
591 * response. Only one request can be sent/queued at a time per @code http_t@
592 * connection.
593 *
594 * Returns the initial HTTP status code, which will be @code HTTP_STATUS_CONTINUE@
595 * on a successful send of the request.
596 *
597 * Note: Unlike @link cupsDoFileRequest@, @link cupsDoIORequest@, and
598 * @link cupsDoRequest@, the request is NOT freed with @link ippDelete@.
599 *
600 * @since CUPS 1.4/OS X 10.6@
601 */
602
603http_status_t				/* O - Initial HTTP status */
604cupsSendRequest(http_t     *http,	/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
605                ipp_t      *request,	/* I - IPP request */
606                const char *resource,	/* I - Resource path */
607		size_t     length)	/* I - Length of data to follow or @code CUPS_LENGTH_VARIABLE@ */
608{
609  http_status_t		status;		/* Status of HTTP request */
610  int			got_status;	/* Did we get the status? */
611  ipp_state_t		state;		/* State of IPP processing */
612  http_status_t		expect;		/* Expect: header to use */
613
614
615  DEBUG_printf(("cupsSendRequest(http=%p, request=%p(%s), resource=\"%s\", "
616                "length=" CUPS_LLFMT ")", http, request,
617		request ? ippOpString(request->request.op.operation_id) : "?",
618		resource, CUPS_LLCAST length));
619
620 /*
621  * Range check input...
622  */
623
624  if (!request || !resource)
625  {
626    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
627
628    return (HTTP_STATUS_ERROR);
629  }
630
631 /*
632  * Get the default connection as needed...
633  */
634
635  if (!http)
636    if ((http = _cupsConnect()) == NULL)
637      return (HTTP_STATUS_SERVICE_UNAVAILABLE);
638
639 /*
640  * If the prior request was not flushed out, do so now...
641  */
642
643  if (http->state == HTTP_STATE_GET_SEND ||
644      http->state == HTTP_STATE_POST_SEND)
645  {
646    DEBUG_puts("2cupsSendRequest: Flush prior response.");
647    httpFlush(http);
648  }
649  else if (http->state != HTTP_STATE_WAITING)
650  {
651    DEBUG_printf(("1cupsSendRequest: Unknown HTTP state (%d), "
652                  "reconnecting.", http->state));
653    if (httpReconnect2(http, 30000, NULL))
654      return (HTTP_STATUS_ERROR);
655  }
656
657#ifdef HAVE_SSL
658 /*
659  * See if we have an auth-info attribute and are communicating over
660  * a non-local link.  If so, encrypt the link so that we can pass
661  * the authentication information securely...
662  */
663
664  if (ippFindAttribute(request, "auth-info", IPP_TAG_TEXT) &&
665      !httpAddrLocalhost(http->hostaddr) && !http->tls &&
666      httpEncryption(http, HTTP_ENCRYPTION_REQUIRED))
667  {
668    DEBUG_puts("1cupsSendRequest: Unable to encrypt connection.");
669    return (HTTP_STATUS_SERVICE_UNAVAILABLE);
670  }
671#endif /* HAVE_SSL */
672
673 /*
674  * Reconnect if the last response had a "Connection: close"...
675  */
676
677  if (!_cups_strcasecmp(http->fields[HTTP_FIELD_CONNECTION], "close"))
678  {
679    DEBUG_puts("2cupsSendRequest: Connection: close");
680    httpClearFields(http);
681    if (httpReconnect2(http, 30000, NULL))
682    {
683      DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
684      return (HTTP_STATUS_SERVICE_UNAVAILABLE);
685    }
686  }
687
688 /*
689  * Loop until we can send the request without authorization problems.
690  */
691
692  expect = HTTP_STATUS_CONTINUE;
693
694  for (;;)
695  {
696    DEBUG_puts("2cupsSendRequest: Setup...");
697
698   /*
699    * Setup the HTTP variables needed...
700    */
701
702    httpClearFields(http);
703    httpSetExpect(http, expect);
704    httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
705    httpSetLength(http, length);
706
707#ifdef HAVE_GSSAPI
708    if (http->authstring && !strncmp(http->authstring, "Negotiate", 9))
709    {
710     /*
711      * Do not use cached Kerberos credentials since they will look like a
712      * "replay" attack...
713      */
714
715      _cupsSetNegotiateAuthString(http, "POST", resource);
716    }
717#endif /* HAVE_GSSAPI */
718
719    httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring);
720
721    DEBUG_printf(("2cupsSendRequest: authstring=\"%s\"", http->authstring));
722
723   /*
724    * Try the request...
725    */
726
727    DEBUG_puts("2cupsSendRequest: Sending HTTP POST...");
728
729    if (httpPost(http, resource))
730    {
731      DEBUG_puts("2cupsSendRequest: POST failed, reconnecting.");
732      if (httpReconnect2(http, 30000, NULL))
733      {
734        DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
735        return (HTTP_STATUS_SERVICE_UNAVAILABLE);
736      }
737      else
738        continue;
739    }
740
741   /*
742    * Send the IPP data...
743    */
744
745    DEBUG_puts("2cupsSendRequest: Writing IPP request...");
746
747    request->state = IPP_STATE_IDLE;
748    status         = HTTP_STATUS_CONTINUE;
749    got_status     = 0;
750
751    while ((state = ippWrite(http, request)) != IPP_STATE_DATA)
752      if (state == IPP_STATE_ERROR)
753	break;
754      else if (httpCheck(http))
755      {
756        got_status = 1;
757
758        _httpUpdate(http, &status);
759	if (status >= HTTP_STATUS_MULTIPLE_CHOICES)
760	  break;
761      }
762
763    if (state == IPP_STATE_ERROR)
764    {
765      DEBUG_puts("1cupsSendRequest: Unable to send IPP request.");
766
767      http->status = HTTP_STATUS_ERROR;
768      http->state  = HTTP_STATE_WAITING;
769
770      return (HTTP_STATUS_ERROR);
771    }
772
773   /*
774    * Wait up to 1 second to get the 100-continue response as needed...
775    */
776
777    if (!got_status)
778    {
779      if (expect == HTTP_STATUS_CONTINUE)
780      {
781	DEBUG_puts("2cupsSendRequest: Waiting for 100-continue...");
782
783	if (httpWait(http, 1000))
784	  _httpUpdate(http, &status);
785      }
786      else if (httpCheck(http))
787	_httpUpdate(http, &status);
788    }
789
790    DEBUG_printf(("2cupsSendRequest: status=%d", status));
791
792   /*
793    * Process the current HTTP status...
794    */
795
796    if (status >= HTTP_STATUS_MULTIPLE_CHOICES)
797    {
798      int temp_status;			/* Temporary status */
799
800      _cupsSetHTTPError(status);
801
802      do
803      {
804	temp_status = httpUpdate(http);
805      }
806      while (temp_status != HTTP_STATUS_ERROR &&
807             http->state == HTTP_STATE_POST_RECV);
808
809      httpFlush(http);
810    }
811
812    switch (status)
813    {
814      case HTTP_STATUS_CONTINUE :
815      case HTTP_STATUS_OK :
816      case HTTP_STATUS_ERROR :
817          DEBUG_printf(("1cupsSendRequest: Returning %d.", status));
818          return (status);
819
820      case HTTP_STATUS_UNAUTHORIZED :
821          if (cupsDoAuthentication(http, "POST", resource))
822	  {
823            DEBUG_puts("1cupsSendRequest: Returning HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED.");
824	    return (HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED);
825	  }
826
827          DEBUG_puts("2cupsSendRequest: Reconnecting after HTTP_STATUS_UNAUTHORIZED.");
828
829	  if (httpReconnect2(http, 30000, NULL))
830	  {
831	    DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
832	    return (HTTP_STATUS_SERVICE_UNAVAILABLE);
833	  }
834	  break;
835
836#ifdef HAVE_SSL
837      case HTTP_STATUS_UPGRADE_REQUIRED :
838	 /*
839	  * Flush any error message, reconnect, and then upgrade with
840	  * encryption...
841	  */
842
843          DEBUG_puts("2cupsSendRequest: Reconnecting after "
844	             "HTTP_STATUS_UPGRADE_REQUIRED.");
845
846	  if (httpReconnect2(http, 30000, NULL))
847	  {
848	    DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
849	    return (HTTP_STATUS_SERVICE_UNAVAILABLE);
850	  }
851
852	  DEBUG_puts("2cupsSendRequest: Upgrading to TLS.");
853	  if (httpEncryption(http, HTTP_ENCRYPTION_REQUIRED))
854	  {
855	    DEBUG_puts("1cupsSendRequest: Unable to encrypt connection.");
856	    return (HTTP_STATUS_SERVICE_UNAVAILABLE);
857	  }
858	  break;
859#endif /* HAVE_SSL */
860
861      case HTTP_STATUS_EXPECTATION_FAILED :
862	 /*
863	  * Don't try using the Expect: header the next time around...
864	  */
865
866	  expect = (http_status_t)0;
867
868          DEBUG_puts("2cupsSendRequest: Reconnecting after "
869	             "HTTP_EXPECTATION_FAILED.");
870
871	  if (httpReconnect2(http, 30000, NULL))
872	  {
873	    DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
874	    return (HTTP_STATUS_SERVICE_UNAVAILABLE);
875	  }
876	  break;
877
878      default :
879         /*
880	  * Some other error...
881	  */
882
883	  return (status);
884    }
885  }
886}
887
888
889/*
890 * 'cupsWriteRequestData()' - Write additional data after an IPP request.
891 *
892 * This function is used after @link cupsSendRequest@ to provide a PPD and
893 * after @link cupsStartDocument@ to provide a document file.
894 *
895 * @since CUPS 1.4/OS X 10.6@
896 */
897
898http_status_t				/* O - @code HTTP_STATUS_CONTINUE@ if OK or HTTP status on error */
899cupsWriteRequestData(
900    http_t     *http,			/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
901    const char *buffer,			/* I - Bytes to write */
902    size_t     length)			/* I - Number of bytes to write */
903{
904  int	wused;				/* Previous bytes in buffer */
905
906
907 /*
908  * Get the default connection as needed...
909  */
910
911  DEBUG_printf(("cupsWriteRequestData(http=%p, buffer=%p, "
912                "length=" CUPS_LLFMT ")", http, buffer, CUPS_LLCAST length));
913
914  if (!http)
915  {
916    _cups_globals_t *cg = _cupsGlobals();
917					/* Pointer to library globals */
918
919    if ((http = cg->http) == NULL)
920    {
921      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No active connection"), 1);
922      DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_ERROR.");
923      return (HTTP_STATUS_ERROR);
924    }
925  }
926
927 /*
928  * Then write to the HTTP connection...
929  */
930
931  wused = http->wused;
932
933  if (httpWrite2(http, buffer, length) < 0)
934  {
935    DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_ERROR.");
936    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(http->error), 0);
937    return (HTTP_STATUS_ERROR);
938  }
939
940 /*
941  * Finally, check if we have any pending data from the server...
942  */
943
944  if (length >= HTTP_MAX_BUFFER ||
945      http->wused < wused ||
946      (wused > 0 && http->wused == length))
947  {
948   /*
949    * We've written something to the server, so check for response data...
950    */
951
952    if (_httpWait(http, 0, 1))
953    {
954      http_status_t	status;		/* Status from _httpUpdate */
955
956      _httpUpdate(http, &status);
957      if (status >= HTTP_STATUS_MULTIPLE_CHOICES)
958      {
959        _cupsSetHTTPError(status);
960
961	do
962	{
963	  status = httpUpdate(http);
964	}
965	while (status != HTTP_STATUS_ERROR && http->state == HTTP_STATE_POST_RECV);
966
967        httpFlush(http);
968      }
969
970      DEBUG_printf(("1cupsWriteRequestData: Returning %d.\n", status));
971      return (status);
972    }
973  }
974
975  DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_CONTINUE.");
976  return (HTTP_STATUS_CONTINUE);
977}
978
979
980/*
981 * '_cupsConnect()' - Get the default server connection...
982 */
983
984http_t *				/* O - HTTP connection */
985_cupsConnect(void)
986{
987  _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
988
989
990 /*
991  * See if we are connected to the same server...
992  */
993
994  if (cg->http)
995  {
996   /*
997    * Compare the connection hostname, port, and encryption settings to
998    * the cached defaults; these were initialized the first time we
999    * connected...
1000    */
1001
1002    if (strcmp(cg->http->hostname, cg->server) ||
1003        cg->ipp_port != httpAddrPort(cg->http->hostaddr) ||
1004        (cg->http->encryption != cg->encryption &&
1005	 cg->http->encryption == HTTP_ENCRYPTION_NEVER))
1006    {
1007     /*
1008      * Need to close the current connection because something has changed...
1009      */
1010
1011      httpClose(cg->http);
1012      cg->http = NULL;
1013    }
1014    else
1015    {
1016     /*
1017      * Same server, see if the connection is still established...
1018      */
1019
1020      char	ch;			/* Connection check byte */
1021      ssize_t	n;			/* Number of bytes */
1022
1023#ifdef WIN32
1024      if ((n = recv(cg->http->fd, &ch, 1, MSG_PEEK)) == 0 ||
1025          (n < 0 && WSAGetLastError() != WSAEWOULDBLOCK))
1026#else
1027      if ((n = recv(cg->http->fd, &ch, 1, MSG_PEEK | MSG_DONTWAIT)) == 0 ||
1028          (n < 0 && errno != EWOULDBLOCK))
1029#endif /* WIN32 */
1030      {
1031       /*
1032        * Nope, close the connection...
1033        */
1034
1035	httpClose(cg->http);
1036	cg->http = NULL;
1037      }
1038    }
1039  }
1040
1041 /*
1042  * (Re)connect as needed...
1043  */
1044
1045  if (!cg->http)
1046  {
1047    if ((cg->http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC,
1048				 cupsEncryption(), 1, 30000, NULL)) == NULL)
1049    {
1050      if (errno)
1051        _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, NULL, 0);
1052      else
1053        _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE,
1054	              _("Unable to connect to host."), 1);
1055    }
1056  }
1057
1058 /*
1059  * Return the cached connection...
1060  */
1061
1062  return (cg->http);
1063}
1064
1065
1066/*
1067 * '_cupsSetError()' - Set the last IPP status code and status-message.
1068 */
1069
1070void
1071_cupsSetError(ipp_status_t status,	/* I - IPP status code */
1072              const char   *message,	/* I - status-message value */
1073	      int          localize)	/* I - Localize the message? */
1074{
1075  _cups_globals_t	*cg;		/* Global data */
1076
1077
1078  if (!message && errno)
1079  {
1080    message  = strerror(errno);
1081    localize = 0;
1082  }
1083
1084  cg             = _cupsGlobals();
1085  cg->last_error = status;
1086
1087  if (cg->last_status_message)
1088  {
1089    _cupsStrFree(cg->last_status_message);
1090
1091    cg->last_status_message = NULL;
1092  }
1093
1094  if (message)
1095  {
1096    if (localize)
1097    {
1098     /*
1099      * Get the message catalog...
1100      */
1101
1102      if (!cg->lang_default)
1103	cg->lang_default = cupsLangDefault();
1104
1105      cg->last_status_message = _cupsStrAlloc(_cupsLangString(cg->lang_default,
1106                                                              message));
1107    }
1108    else
1109      cg->last_status_message = _cupsStrAlloc(message);
1110  }
1111
1112  DEBUG_printf(("4_cupsSetError: last_error=%s, last_status_message=\"%s\"",
1113                ippErrorString(cg->last_error), cg->last_status_message));
1114}
1115
1116
1117/*
1118 * '_cupsSetHTTPError()' - Set the last error using the HTTP status.
1119 */
1120
1121void
1122_cupsSetHTTPError(http_status_t status)	/* I - HTTP status code */
1123{
1124  switch (status)
1125  {
1126    case HTTP_STATUS_NOT_FOUND :
1127	_cupsSetError(IPP_STATUS_ERROR_NOT_FOUND, httpStatus(status), 0);
1128	break;
1129
1130    case HTTP_STATUS_UNAUTHORIZED :
1131	_cupsSetError(IPP_STATUS_ERROR_NOT_AUTHENTICATED, httpStatus(status), 0);
1132	break;
1133
1134    case HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED :
1135	_cupsSetError(IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED, httpStatus(status), 0);
1136	break;
1137
1138    case HTTP_STATUS_FORBIDDEN :
1139	_cupsSetError(IPP_STATUS_ERROR_FORBIDDEN, httpStatus(status), 0);
1140	break;
1141
1142    case HTTP_STATUS_BAD_REQUEST :
1143	_cupsSetError(IPP_STATUS_ERROR_BAD_REQUEST, httpStatus(status), 0);
1144	break;
1145
1146    case HTTP_STATUS_REQUEST_TOO_LARGE :
1147	_cupsSetError(IPP_STATUS_ERROR_REQUEST_VALUE, httpStatus(status), 0);
1148	break;
1149
1150    case HTTP_STATUS_NOT_IMPLEMENTED :
1151	_cupsSetError(IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED, httpStatus(status), 0);
1152	break;
1153
1154    case HTTP_STATUS_NOT_SUPPORTED :
1155	_cupsSetError(IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED, httpStatus(status), 0);
1156	break;
1157
1158    case HTTP_STATUS_UPGRADE_REQUIRED :
1159	_cupsSetError(IPP_STATUS_ERROR_CUPS_UPGRADE_REQUIRED, httpStatus(status), 0);
1160        break;
1161
1162    case HTTP_STATUS_CUPS_PKI_ERROR :
1163	_cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, httpStatus(status), 0);
1164        break;
1165
1166    case HTTP_STATUS_ERROR :
1167	_cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
1168        break;
1169
1170    default :
1171	DEBUG_printf(("4_cupsSetHTTPError: HTTP error %d mapped to "
1172	              "IPP_STATUS_ERROR_SERVICE_UNAVAILABLE!", status));
1173	_cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, httpStatus(status), 0);
1174	break;
1175  }
1176}
1177
1178
1179/*
1180 * End of "$Id: request.c 11737 2014-03-26 20:48:24Z msweet $".
1181 */
1182