]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/request.c
Merge changes from CUPS 1.6svn-r10510.
[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-2012 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/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 ((status = cupsWriteRequestData(http, buffer, bytes))
252 != HTTP_CONTINUE)
253 break;
254 }
255 }
256
257 /*
258 * Get the server's response...
259 */
260
261 if (status != HTTP_ERROR)
262 {
263 response = cupsGetResponse(http, resource);
264 status = httpGetStatus(http);
265 }
266
267 DEBUG_printf(("2cupsDoIORequest: status=%d", status));
268
269 if (status == HTTP_ERROR ||
270 (status >= HTTP_BAD_REQUEST && status != HTTP_UNAUTHORIZED &&
271 status != HTTP_UPGRADE_REQUIRED))
272 {
273 _cupsSetHTTPError(status);
274 break;
275 }
276
277 if (response && outfile >= 0)
278 {
279 /*
280 * Write trailing data to file...
281 */
282
283 while ((bytes = (int)httpRead2(http, buffer, sizeof(buffer))) > 0)
284 if (write(outfile, buffer, bytes) < bytes)
285 break;
286 }
287
288 if (http->state != HTTP_WAITING)
289 {
290 /*
291 * Flush any remaining data...
292 */
293
294 httpFlush(http);
295 }
296 }
297
298 /*
299 * Delete the original request and return the response...
300 */
301
302 ippDelete(request);
303
304 return (response);
305 }
306
307
308 /*
309 * 'cupsDoRequest()' - Do an IPP request.
310 *
311 * This function sends the IPP request to the specified server, retrying
312 * and authenticating as necessary. The request is freed with ippDelete()
313 * after receiving a valid IPP response.
314 */
315
316 ipp_t * /* O - Response data */
317 cupsDoRequest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
318 ipp_t *request, /* I - IPP request */
319 const char *resource) /* I - HTTP resource for POST */
320 {
321 DEBUG_printf(("cupsDoRequest(http=%p, request=%p(%s), resource=\"%s\")",
322 http, request,
323 request ? ippOpString(request->request.op.operation_id) : "?",
324 resource));
325
326 return (cupsDoIORequest(http, request, resource, -1, -1));
327 }
328
329
330 /*
331 * 'cupsGetResponse()' - Get a response to an IPP request.
332 *
333 * Use this function to get the response for an IPP request sent using
334 * cupsSendDocument() or cupsSendRequest(). For requests that return
335 * additional data, use httpRead() after getting a successful response,
336 * otherwise call httpFlush() to complete the response processing.
337 *
338 * @since CUPS 1.4/OS X 10.6@
339 */
340
341 ipp_t * /* O - Response or @code NULL@ on HTTP error */
342 cupsGetResponse(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
343 const char *resource) /* I - HTTP resource for POST */
344 {
345 http_status_t status; /* HTTP status */
346 ipp_state_t state; /* IPP read state */
347 ipp_t *response = NULL; /* IPP response */
348
349
350 DEBUG_printf(("cupsGetResponse(http=%p, resource=\"%s\")", http, resource));
351
352 /*
353 * Connect to the default server as needed...
354 */
355
356 if (!http)
357 http = _cupsConnect();
358
359 if (!http || (http->state != HTTP_POST_RECV && http->state != HTTP_POST_SEND))
360 return (NULL);
361
362 /*
363 * Check for an unfinished chunked request...
364 */
365
366 if (http->data_encoding == HTTP_ENCODE_CHUNKED)
367 {
368 /*
369 * Send a 0-length chunk to finish off the request...
370 */
371
372 DEBUG_puts("2cupsGetResponse: Finishing chunked POST...");
373
374 if (httpWrite2(http, "", 0) < 0)
375 return (NULL);
376 }
377
378 /*
379 * Wait for a response from the server...
380 */
381
382 DEBUG_printf(("2cupsGetResponse: Update loop, http->status=%d...",
383 http->status));
384
385 do
386 {
387 status = httpUpdate(http);
388 }
389 while (status != HTTP_ERROR && http->state == HTTP_POST_RECV);
390
391 DEBUG_printf(("2cupsGetResponse: status=%d", status));
392
393 if (status == HTTP_OK)
394 {
395 /*
396 * Get the IPP response...
397 */
398
399 response = ippNew();
400
401 while ((state = ippRead(http, response)) != IPP_DATA)
402 if (state == IPP_ERROR)
403 break;
404
405 if (state == IPP_ERROR)
406 {
407 /*
408 * Flush remaining data and delete the response...
409 */
410
411 DEBUG_puts("1cupsGetResponse: IPP read error!");
412
413 httpFlush(http);
414
415 ippDelete(response);
416 response = NULL;
417
418 http->status = status = HTTP_ERROR;
419 http->error = EINVAL;
420 }
421 }
422 else if (status != HTTP_ERROR)
423 {
424 /*
425 * Flush any error message...
426 */
427
428 httpFlush(http);
429
430 /*
431 * Then handle encryption and authentication...
432 */
433
434 if (status == HTTP_UNAUTHORIZED)
435 {
436 /*
437 * See if we can do authentication...
438 */
439
440 DEBUG_puts("2cupsGetResponse: Need authorization...");
441
442 if (!cupsDoAuthentication(http, "POST", resource))
443 httpReconnect(http);
444 else
445 http->status = status = HTTP_AUTHORIZATION_CANCELED;
446 }
447
448 #ifdef HAVE_SSL
449 else if (status == HTTP_UPGRADE_REQUIRED)
450 {
451 /*
452 * Force a reconnect with encryption...
453 */
454
455 DEBUG_puts("2cupsGetResponse: Need encryption...");
456
457 if (!httpReconnect(http))
458 httpEncryption(http, HTTP_ENCRYPT_REQUIRED);
459 }
460 #endif /* HAVE_SSL */
461 }
462
463 if (response)
464 {
465 ipp_attribute_t *attr; /* status-message attribute */
466
467
468 attr = ippFindAttribute(response, "status-message", IPP_TAG_TEXT);
469
470 DEBUG_printf(("1cupsGetResponse: status-code=%s, status-message=\"%s\"",
471 ippErrorString(response->request.status.status_code),
472 attr ? attr->values[0].string.text : ""));
473
474 _cupsSetError(response->request.status.status_code,
475 attr ? attr->values[0].string.text :
476 ippErrorString(response->request.status.status_code), 0);
477 }
478
479 return (response);
480 }
481
482
483 /*
484 * 'cupsLastError()' - Return the last IPP status code.
485 */
486
487 ipp_status_t /* O - IPP status code from last request */
488 cupsLastError(void)
489 {
490 return (_cupsGlobals()->last_error);
491 }
492
493
494 /*
495 * 'cupsLastErrorString()' - Return the last IPP status-message.
496 *
497 * @since CUPS 1.2/OS X 10.5@
498 */
499
500 const char * /* O - status-message text from last request */
501 cupsLastErrorString(void)
502 {
503 return (_cupsGlobals()->last_status_message);
504 }
505
506
507 /*
508 * '_cupsNextDelay()' - Return the next retry delay value.
509 *
510 * This function currently returns the Fibonacci sequence 1 1 2 3 5 8.
511 *
512 * Pass 0 for the current delay value to initialize the sequence.
513 */
514
515 int /* O - Next delay value */
516 _cupsNextDelay(int current, /* I - Current delay value or 0 */
517 int *previous) /* IO - Previous delay value */
518 {
519 int next; /* Next delay value */
520
521
522 if (current > 0)
523 {
524 next = (current + *previous) % 12;
525 *previous = next < current ? 0 : current;
526 }
527 else
528 {
529 next = 1;
530 *previous = 0;
531 }
532
533 return (next);
534 }
535
536
537 /*
538 * 'cupsReadResponseData()' - Read additional data after the IPP response.
539 *
540 * This function is used after cupsGetResponse() to read the PPD or document
541 * files for CUPS_GET_PPD and CUPS_GET_DOCUMENT requests, respectively.
542 *
543 * @since CUPS 1.4/OS X 10.6@
544 */
545
546 ssize_t /* O - Bytes read, 0 on EOF, -1 on error */
547 cupsReadResponseData(
548 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
549 char *buffer, /* I - Buffer to use */
550 size_t length) /* I - Number of bytes to read */
551 {
552 /*
553 * Get the default connection as needed...
554 */
555
556 DEBUG_printf(("cupsReadResponseData(http=%p, buffer=%p, "
557 "length=" CUPS_LLFMT ")", http, buffer, CUPS_LLCAST length));
558
559 if (!http)
560 {
561 _cups_globals_t *cg = _cupsGlobals();
562 /* Pointer to library globals */
563
564 if ((http = cg->http) == NULL)
565 {
566 _cupsSetError(IPP_INTERNAL_ERROR, _("No active connection"), 1);
567 return (-1);
568 }
569 }
570
571 /*
572 * Then read from the HTTP connection...
573 */
574
575 return (httpRead2(http, buffer, length));
576 }
577
578
579 /*
580 * 'cupsSendRequest()' - Send an IPP request.
581 *
582 * Use httpWrite() to write any additional data (document, PPD file, etc.)
583 * for the request, cupsGetResponse() to get the IPP response, and httpRead()
584 * to read any additional data following the response. Only one request can be
585 * sent/queued at a time.
586 *
587 * Unlike cupsDoFileRequest(), cupsDoIORequest(), and cupsDoRequest(), the
588 * request is not freed.
589 *
590 * @since CUPS 1.4/OS X 10.6@
591 */
592
593 http_status_t /* O - Initial HTTP status */
594 cupsSendRequest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
595 ipp_t *request, /* I - IPP request */
596 const char *resource, /* I - Resource path */
597 size_t length) /* I - Length of data to follow or @code CUPS_LENGTH_VARIABLE@ */
598 {
599 http_status_t status; /* Status of HTTP request */
600 int got_status; /* Did we get the status? */
601 ipp_state_t state; /* State of IPP processing */
602 http_status_t expect; /* Expect: header to use */
603
604
605 DEBUG_printf(("cupsSendRequest(http=%p, request=%p(%s), resource=\"%s\", "
606 "length=" CUPS_LLFMT ")", http, request,
607 request ? ippOpString(request->request.op.operation_id) : "?",
608 resource, CUPS_LLCAST length));
609
610 /*
611 * Range check input...
612 */
613
614 if (!request || !resource)
615 {
616 _cupsSetError(IPP_INTERNAL_ERROR, strerror(EINVAL), 0);
617
618 return (HTTP_ERROR);
619 }
620
621 /*
622 * Get the default connection as needed...
623 */
624
625 if (!http)
626 if ((http = _cupsConnect()) == NULL)
627 return (HTTP_SERVICE_UNAVAILABLE);
628
629 /*
630 * If the prior request was not flushed out, do so now...
631 */
632
633 if (http->state == HTTP_GET_SEND ||
634 http->state == HTTP_POST_SEND)
635 {
636 DEBUG_puts("2cupsSendRequest: Flush prior response.");
637 httpFlush(http);
638 }
639 else if (http->state != HTTP_WAITING)
640 {
641 DEBUG_printf(("1cupsSendRequest: Unknown HTTP state (%d), "
642 "reconnecting.", http->state));
643 if (httpReconnect(http))
644 return (HTTP_ERROR);
645 }
646
647 #ifdef HAVE_SSL
648 /*
649 * See if we have an auth-info attribute and are communicating over
650 * a non-local link. If so, encrypt the link so that we can pass
651 * the authentication information securely...
652 */
653
654 if (ippFindAttribute(request, "auth-info", IPP_TAG_TEXT) &&
655 !httpAddrLocalhost(http->hostaddr) && !http->tls &&
656 httpEncryption(http, HTTP_ENCRYPT_REQUIRED))
657 {
658 DEBUG_puts("1cupsSendRequest: Unable to encrypt connection.");
659 return (HTTP_SERVICE_UNAVAILABLE);
660 }
661 #endif /* HAVE_SSL */
662
663 /*
664 * Reconnect if the last response had a "Connection: close"...
665 */
666
667 if (!_cups_strcasecmp(http->fields[HTTP_FIELD_CONNECTION], "close"))
668 {
669 DEBUG_puts("2cupsSendRequest: Connection: close");
670 httpClearFields(http);
671 if (httpReconnect(http))
672 {
673 DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
674 return (HTTP_SERVICE_UNAVAILABLE);
675 }
676 }
677
678 /*
679 * Loop until we can send the request without authorization problems.
680 */
681
682 expect = HTTP_CONTINUE;
683
684 for (;;)
685 {
686 DEBUG_puts("2cupsSendRequest: Setup...");
687
688 /*
689 * Setup the HTTP variables needed...
690 */
691
692 httpClearFields(http);
693 httpSetExpect(http, expect);
694 httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
695 httpSetLength(http, length);
696
697 #ifdef HAVE_GSSAPI
698 if (http->authstring && !strncmp(http->authstring, "Negotiate", 9))
699 {
700 /*
701 * Do not use cached Kerberos credentials since they will look like a
702 * "replay" attack...
703 */
704
705 _cupsSetNegotiateAuthString(http, "POST", resource);
706 }
707 #endif /* HAVE_GSSAPI */
708
709 httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring);
710
711 DEBUG_printf(("2cupsSendRequest: authstring=\"%s\"", http->authstring));
712
713 /*
714 * Try the request...
715 */
716
717 DEBUG_puts("2cupsSendRequest: Sending HTTP POST...");
718
719 if (httpPost(http, resource))
720 {
721 DEBUG_puts("2cupsSendRequest: POST failed, reconnecting.");
722 if (httpReconnect(http))
723 {
724 DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
725 return (HTTP_SERVICE_UNAVAILABLE);
726 }
727 else
728 continue;
729 }
730
731 /*
732 * Send the IPP data...
733 */
734
735 DEBUG_puts("2cupsSendRequest: Writing IPP request...");
736
737 request->state = IPP_IDLE;
738 status = HTTP_CONTINUE;
739 got_status = 0;
740
741 while ((state = ippWrite(http, request)) != IPP_DATA)
742 if (state == IPP_ERROR)
743 break;
744 else if (httpCheck(http))
745 {
746 got_status = 1;
747
748 _httpUpdate(http, &status);
749 if (status >= HTTP_MULTIPLE_CHOICES)
750 break;
751 }
752
753 if (state == IPP_ERROR)
754 {
755 DEBUG_puts("1cupsSendRequest: Unable to send IPP request.");
756
757 http->status = HTTP_ERROR;
758 http->state = HTTP_WAITING;
759
760 return (HTTP_ERROR);
761 }
762
763 /*
764 * Wait up to 1 second to get the 100-continue response as needed...
765 */
766
767 if (!got_status)
768 {
769 if (expect == HTTP_CONTINUE)
770 {
771 DEBUG_puts("2cupsSendRequest: Waiting for 100-continue...");
772
773 if (httpWait(http, 1000))
774 _httpUpdate(http, &status);
775 }
776 else if (httpCheck(http))
777 _httpUpdate(http, &status);
778 }
779
780 DEBUG_printf(("2cupsSendRequest: status=%d", status));
781
782 /*
783 * Process the current HTTP status...
784 */
785
786 if (status >= HTTP_MULTIPLE_CHOICES)
787 {
788 _cupsSetHTTPError(status);
789
790 do
791 {
792 status = httpUpdate(http);
793 }
794 while (status != HTTP_ERROR && http->state == HTTP_POST_RECV);
795
796 httpFlush(http);
797 }
798
799 switch (status)
800 {
801 case HTTP_ERROR :
802 case HTTP_CONTINUE :
803 case HTTP_OK :
804 DEBUG_printf(("1cupsSendRequest: Returning %d.", status));
805 return (status);
806
807 case HTTP_UNAUTHORIZED :
808 if (cupsDoAuthentication(http, "POST", resource))
809 {
810 DEBUG_puts("1cupsSendRequest: Returning HTTP_AUTHORIZATION_CANCELED.");
811 return (HTTP_AUTHORIZATION_CANCELED);
812 }
813
814 DEBUG_puts("2cupsSendRequest: Reconnecting after HTTP_UNAUTHORIZED.");
815
816 if (httpReconnect(http))
817 {
818 DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
819 return (HTTP_SERVICE_UNAVAILABLE);
820 }
821 break;
822
823 #ifdef HAVE_SSL
824 case HTTP_UPGRADE_REQUIRED :
825 /*
826 * Flush any error message, reconnect, and then upgrade with
827 * encryption...
828 */
829
830 DEBUG_puts("2cupsSendRequest: Reconnecting after "
831 "HTTP_UPGRADE_REQUIRED.");
832
833 if (httpReconnect(http))
834 {
835 DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
836 return (HTTP_SERVICE_UNAVAILABLE);
837 }
838
839 DEBUG_puts("2cupsSendRequest: Upgrading to TLS.");
840 if (httpEncryption(http, HTTP_ENCRYPT_REQUIRED))
841 {
842 DEBUG_puts("1cupsSendRequest: Unable to encrypt connection.");
843 return (HTTP_SERVICE_UNAVAILABLE);
844 }
845 break;
846 #endif /* HAVE_SSL */
847
848 case HTTP_EXPECTATION_FAILED :
849 /*
850 * Don't try using the Expect: header the next time around...
851 */
852
853 expect = (http_status_t)0;
854
855 DEBUG_puts("2cupsSendRequest: Reconnecting after "
856 "HTTP_EXPECTATION_FAILED.");
857
858 if (httpReconnect(http))
859 {
860 DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
861 return (HTTP_SERVICE_UNAVAILABLE);
862 }
863 break;
864
865 default :
866 /*
867 * Some other error...
868 */
869
870 return (status);
871 }
872 }
873 }
874
875
876 /*
877 * 'cupsWriteRequestData()' - Write additional data after an IPP request.
878 *
879 * This function is used after @link cupsSendRequest@ to provide a PPD and
880 * after @link cupsStartDocument@ to provide a document file.
881 *
882 * @since CUPS 1.4/OS X 10.6@
883 */
884
885 http_status_t /* O - @code HTTP_CONTINUE@ if OK or HTTP status on error */
886 cupsWriteRequestData(
887 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
888 const char *buffer, /* I - Bytes to write */
889 size_t length) /* I - Number of bytes to write */
890 {
891 int wused; /* Previous bytes in buffer */
892
893
894 /*
895 * Get the default connection as needed...
896 */
897
898 DEBUG_printf(("cupsWriteRequestData(http=%p, buffer=%p, "
899 "length=" CUPS_LLFMT ")", http, buffer, CUPS_LLCAST length));
900
901 if (!http)
902 {
903 _cups_globals_t *cg = _cupsGlobals();
904 /* Pointer to library globals */
905
906 if ((http = cg->http) == NULL)
907 {
908 _cupsSetError(IPP_INTERNAL_ERROR, _("No active connection"), 1);
909 DEBUG_puts("1cupsWriteRequestData: Returning HTTP_ERROR.");
910 return (HTTP_ERROR);
911 }
912 }
913
914 /*
915 * Then write to the HTTP connection...
916 */
917
918 wused = http->wused;
919
920 if (httpWrite2(http, buffer, length) < 0)
921 {
922 DEBUG_puts("1cupsWriteRequestData: Returning HTTP_ERROR.");
923 _cupsSetError(IPP_INTERNAL_ERROR, strerror(http->error), 0);
924 return (HTTP_ERROR);
925 }
926
927 /*
928 * Finally, check if we have any pending data from the server...
929 */
930
931 if (length >= HTTP_MAX_BUFFER ||
932 http->wused < wused ||
933 (wused > 0 && http->wused == length))
934 {
935 /*
936 * We've written something to the server, so check for response data...
937 */
938
939 if (_httpWait(http, 0, 1))
940 {
941 http_status_t status; /* Status from _httpUpdate */
942
943 _httpUpdate(http, &status);
944 if (status >= HTTP_MULTIPLE_CHOICES)
945 {
946 _cupsSetHTTPError(status);
947
948 do
949 {
950 status = httpUpdate(http);
951 }
952 while (status != HTTP_ERROR && http->state == HTTP_POST_RECV);
953
954 httpFlush(http);
955 }
956
957 DEBUG_printf(("1cupsWriteRequestData: Returning %d.\n", status));
958 return (status);
959 }
960 }
961
962 DEBUG_puts("1cupsWriteRequestData: Returning HTTP_CONTINUE.");
963 return (HTTP_CONTINUE);
964 }
965
966
967 /*
968 * '_cupsConnect()' - Get the default server connection...
969 */
970
971 http_t * /* O - HTTP connection */
972 _cupsConnect(void)
973 {
974 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
975
976
977 /*
978 * See if we are connected to the same server...
979 */
980
981 if (cg->http)
982 {
983 /*
984 * Compare the connection hostname, port, and encryption settings to
985 * the cached defaults; these were initialized the first time we
986 * connected...
987 */
988
989 if (strcmp(cg->http->hostname, cg->server) ||
990 cg->ipp_port != _httpAddrPort(cg->http->hostaddr) ||
991 (cg->http->encryption != cg->encryption &&
992 cg->http->encryption == HTTP_ENCRYPT_NEVER))
993 {
994 /*
995 * Need to close the current connection because something has changed...
996 */
997
998 httpClose(cg->http);
999 cg->http = NULL;
1000 }
1001 }
1002
1003 /*
1004 * (Re)connect as needed...
1005 */
1006
1007 if (!cg->http)
1008 {
1009 if ((cg->http = httpConnectEncrypt(cupsServer(), ippPort(),
1010 cupsEncryption())) == NULL)
1011 {
1012 if (errno)
1013 _cupsSetError(IPP_SERVICE_UNAVAILABLE, NULL, 0);
1014 else
1015 _cupsSetError(IPP_SERVICE_UNAVAILABLE,
1016 _("Unable to connect to host."), 1);
1017 }
1018 }
1019
1020 /*
1021 * Return the cached connection...
1022 */
1023
1024 return (cg->http);
1025 }
1026
1027
1028 /*
1029 * '_cupsSetError()' - Set the last IPP status code and status-message.
1030 */
1031
1032 void
1033 _cupsSetError(ipp_status_t status, /* I - IPP status code */
1034 const char *message, /* I - status-message value */
1035 int localize) /* I - Localize the message? */
1036 {
1037 _cups_globals_t *cg; /* Global data */
1038
1039
1040 if (!message && errno)
1041 {
1042 message = strerror(errno);
1043 localize = 0;
1044 }
1045
1046 cg = _cupsGlobals();
1047 cg->last_error = status;
1048
1049 if (cg->last_status_message)
1050 {
1051 _cupsStrFree(cg->last_status_message);
1052
1053 cg->last_status_message = NULL;
1054 }
1055
1056 if (message)
1057 {
1058 if (localize)
1059 {
1060 /*
1061 * Get the message catalog...
1062 */
1063
1064 if (!cg->lang_default)
1065 cg->lang_default = cupsLangDefault();
1066
1067 cg->last_status_message = _cupsStrAlloc(_cupsLangString(cg->lang_default,
1068 message));
1069 }
1070 else
1071 cg->last_status_message = _cupsStrAlloc(message);
1072 }
1073
1074 DEBUG_printf(("4_cupsSetError: last_error=%s, last_status_message=\"%s\"",
1075 ippErrorString(cg->last_error), cg->last_status_message));
1076 }
1077
1078
1079 /*
1080 * '_cupsSetHTTPError()' - Set the last error using the HTTP status.
1081 */
1082
1083 void
1084 _cupsSetHTTPError(http_status_t status) /* I - HTTP status code */
1085 {
1086 switch (status)
1087 {
1088 case HTTP_NOT_FOUND :
1089 _cupsSetError(IPP_NOT_FOUND, httpStatus(status), 0);
1090 break;
1091
1092 case HTTP_UNAUTHORIZED :
1093 _cupsSetError(IPP_NOT_AUTHENTICATED, httpStatus(status), 0);
1094 break;
1095
1096 case HTTP_AUTHORIZATION_CANCELED :
1097 _cupsSetError(IPP_AUTHENTICATION_CANCELED, httpStatus(status), 0);
1098 break;
1099
1100 case HTTP_FORBIDDEN :
1101 _cupsSetError(IPP_FORBIDDEN, httpStatus(status), 0);
1102 break;
1103
1104 case HTTP_BAD_REQUEST :
1105 _cupsSetError(IPP_BAD_REQUEST, httpStatus(status), 0);
1106 break;
1107
1108 case HTTP_REQUEST_TOO_LARGE :
1109 _cupsSetError(IPP_REQUEST_VALUE, httpStatus(status), 0);
1110 break;
1111
1112 case HTTP_NOT_IMPLEMENTED :
1113 _cupsSetError(IPP_OPERATION_NOT_SUPPORTED, httpStatus(status), 0);
1114 break;
1115
1116 case HTTP_NOT_SUPPORTED :
1117 _cupsSetError(IPP_VERSION_NOT_SUPPORTED, httpStatus(status), 0);
1118 break;
1119
1120 case HTTP_UPGRADE_REQUIRED :
1121 _cupsSetError(IPP_UPGRADE_REQUIRED, httpStatus(status), 0);
1122 break;
1123
1124 case HTTP_PKI_ERROR :
1125 _cupsSetError(IPP_PKI_ERROR, httpStatus(status), 0);
1126 break;
1127
1128 case HTTP_ERROR :
1129 _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno), 0);
1130 break;
1131
1132 default :
1133 DEBUG_printf(("4_cupsSetHTTPError: HTTP error %d mapped to "
1134 "IPP_SERVICE_UNAVAILABLE!", status));
1135 _cupsSetError(IPP_SERVICE_UNAVAILABLE, httpStatus(status), 0);
1136 break;
1137 }
1138 }
1139
1140
1141 /*
1142 * End of "$Id: request.c 7946 2008-09-16 23:27:54Z mike $".
1143 */