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