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