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