]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/request.c
881bffd5b929c42776b2f35358a8bd67532f724c
[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 {
751 if (httpCheck(http))
752 {
753 got_status = 1;
754
755 _httpUpdate(http, &status);
756 if (status >= HTTP_STATUS_MULTIPLE_CHOICES)
757 break;
758 }
759 else if (state == IPP_STATE_ERROR)
760 break;
761 }
762
763 if (state == IPP_STATE_ERROR)
764 {
765 /*
766 * We weren't able to send the IPP request. But did we already get a HTTP
767 * error status?
768 */
769
770 if (!got_status || status < HTTP_STATUS_MULTIPLE_CHOICES)
771 {
772 /*
773 * No, something else went wrong.
774 */
775
776 DEBUG_puts("1cupsSendRequest: Unable to send IPP request.");
777
778 http->status = HTTP_STATUS_ERROR;
779 http->state = HTTP_STATE_WAITING;
780
781 return (HTTP_STATUS_ERROR);
782 }
783 }
784
785 /*
786 * Wait up to 1 second to get the 100-continue response as needed...
787 */
788
789 if (!got_status)
790 {
791 if (expect == HTTP_STATUS_CONTINUE)
792 {
793 DEBUG_puts("2cupsSendRequest: Waiting for 100-continue...");
794
795 if (httpWait(http, 1000))
796 _httpUpdate(http, &status);
797 }
798 else if (httpCheck(http))
799 _httpUpdate(http, &status);
800 }
801
802 DEBUG_printf(("2cupsSendRequest: status=%d", status));
803
804 /*
805 * Process the current HTTP status...
806 */
807
808 if (status >= HTTP_STATUS_MULTIPLE_CHOICES)
809 {
810 int temp_status; /* Temporary status */
811
812 _cupsSetHTTPError(status);
813
814 do
815 {
816 temp_status = httpUpdate(http);
817 }
818 while (temp_status != HTTP_STATUS_ERROR &&
819 http->state == HTTP_STATE_POST_RECV);
820
821 httpFlush(http);
822 }
823
824 switch (status)
825 {
826 case HTTP_STATUS_CONTINUE :
827 case HTTP_STATUS_OK :
828 case HTTP_STATUS_ERROR :
829 DEBUG_printf(("1cupsSendRequest: Returning %d.", status));
830 return (status);
831
832 case HTTP_STATUS_UNAUTHORIZED :
833 if (cupsDoAuthentication(http, "POST", resource))
834 {
835 DEBUG_puts("1cupsSendRequest: Returning HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED.");
836 return (HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED);
837 }
838
839 DEBUG_puts("2cupsSendRequest: Reconnecting after HTTP_STATUS_UNAUTHORIZED.");
840
841 if (httpReconnect2(http, 30000, NULL))
842 {
843 DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
844 return (HTTP_STATUS_SERVICE_UNAVAILABLE);
845 }
846 break;
847
848 #ifdef HAVE_SSL
849 case HTTP_STATUS_UPGRADE_REQUIRED :
850 /*
851 * Flush any error message, reconnect, and then upgrade with
852 * encryption...
853 */
854
855 DEBUG_puts("2cupsSendRequest: Reconnecting after "
856 "HTTP_STATUS_UPGRADE_REQUIRED.");
857
858 if (httpReconnect2(http, 30000, NULL))
859 {
860 DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
861 return (HTTP_STATUS_SERVICE_UNAVAILABLE);
862 }
863
864 DEBUG_puts("2cupsSendRequest: Upgrading to TLS.");
865 if (httpEncryption(http, HTTP_ENCRYPTION_REQUIRED))
866 {
867 DEBUG_puts("1cupsSendRequest: Unable to encrypt connection.");
868 return (HTTP_STATUS_SERVICE_UNAVAILABLE);
869 }
870 break;
871 #endif /* HAVE_SSL */
872
873 case HTTP_STATUS_EXPECTATION_FAILED :
874 /*
875 * Don't try using the Expect: header the next time around...
876 */
877
878 expect = (http_status_t)0;
879
880 DEBUG_puts("2cupsSendRequest: Reconnecting after "
881 "HTTP_EXPECTATION_FAILED.");
882
883 if (httpReconnect2(http, 30000, NULL))
884 {
885 DEBUG_puts("1cupsSendRequest: Unable to reconnect.");
886 return (HTTP_STATUS_SERVICE_UNAVAILABLE);
887 }
888 break;
889
890 default :
891 /*
892 * Some other error...
893 */
894
895 return (status);
896 }
897 }
898 }
899
900
901 /*
902 * 'cupsWriteRequestData()' - Write additional data after an IPP request.
903 *
904 * This function is used after @link cupsSendRequest@ to provide a PPD and
905 * after @link cupsStartDocument@ to provide a document file.
906 *
907 * @since CUPS 1.4/OS X 10.6@
908 */
909
910 http_status_t /* O - @code HTTP_STATUS_CONTINUE@ if OK or HTTP status on error */
911 cupsWriteRequestData(
912 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
913 const char *buffer, /* I - Bytes to write */
914 size_t length) /* I - Number of bytes to write */
915 {
916 int wused; /* Previous bytes in buffer */
917
918
919 /*
920 * Get the default connection as needed...
921 */
922
923 DEBUG_printf(("cupsWriteRequestData(http=%p, buffer=%p, "
924 "length=" CUPS_LLFMT ")", http, buffer, CUPS_LLCAST length));
925
926 if (!http)
927 {
928 _cups_globals_t *cg = _cupsGlobals();
929 /* Pointer to library globals */
930
931 if ((http = cg->http) == NULL)
932 {
933 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No active connection"), 1);
934 DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_ERROR.");
935 return (HTTP_STATUS_ERROR);
936 }
937 }
938
939 /*
940 * Then write to the HTTP connection...
941 */
942
943 wused = http->wused;
944
945 if (httpWrite2(http, buffer, length) < 0)
946 {
947 DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_ERROR.");
948 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(http->error), 0);
949 return (HTTP_STATUS_ERROR);
950 }
951
952 /*
953 * Finally, check if we have any pending data from the server...
954 */
955
956 if (length >= HTTP_MAX_BUFFER ||
957 http->wused < wused ||
958 (wused > 0 && (size_t)http->wused == length))
959 {
960 /*
961 * We've written something to the server, so check for response data...
962 */
963
964 if (_httpWait(http, 0, 1))
965 {
966 http_status_t status; /* Status from _httpUpdate */
967
968 _httpUpdate(http, &status);
969 if (status >= HTTP_STATUS_MULTIPLE_CHOICES)
970 {
971 _cupsSetHTTPError(status);
972
973 do
974 {
975 status = httpUpdate(http);
976 }
977 while (status != HTTP_STATUS_ERROR && http->state == HTTP_STATE_POST_RECV);
978
979 httpFlush(http);
980 }
981
982 DEBUG_printf(("1cupsWriteRequestData: Returning %d.\n", status));
983 return (status);
984 }
985 }
986
987 DEBUG_puts("1cupsWriteRequestData: Returning HTTP_STATUS_CONTINUE.");
988 return (HTTP_STATUS_CONTINUE);
989 }
990
991
992 /*
993 * '_cupsConnect()' - Get the default server connection...
994 */
995
996 http_t * /* O - HTTP connection */
997 _cupsConnect(void)
998 {
999 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
1000
1001
1002 /*
1003 * See if we are connected to the same server...
1004 */
1005
1006 if (cg->http)
1007 {
1008 /*
1009 * Compare the connection hostname, port, and encryption settings to
1010 * the cached defaults; these were initialized the first time we
1011 * connected...
1012 */
1013
1014 if (strcmp(cg->http->hostname, cg->server) ||
1015 cg->ipp_port != httpAddrPort(cg->http->hostaddr) ||
1016 (cg->http->encryption != cg->encryption &&
1017 cg->http->encryption == HTTP_ENCRYPTION_NEVER))
1018 {
1019 /*
1020 * Need to close the current connection because something has changed...
1021 */
1022
1023 httpClose(cg->http);
1024 cg->http = NULL;
1025 }
1026 else
1027 {
1028 /*
1029 * Same server, see if the connection is still established...
1030 */
1031
1032 char ch; /* Connection check byte */
1033 ssize_t n; /* Number of bytes */
1034
1035 #ifdef WIN32
1036 if ((n = recv(cg->http->fd, &ch, 1, MSG_PEEK)) == 0 ||
1037 (n < 0 && WSAGetLastError() != WSAEWOULDBLOCK))
1038 #else
1039 if ((n = recv(cg->http->fd, &ch, 1, MSG_PEEK | MSG_DONTWAIT)) == 0 ||
1040 (n < 0 && errno != EWOULDBLOCK))
1041 #endif /* WIN32 */
1042 {
1043 /*
1044 * Nope, close the connection...
1045 */
1046
1047 httpClose(cg->http);
1048 cg->http = NULL;
1049 }
1050 }
1051 }
1052
1053 /*
1054 * (Re)connect as needed...
1055 */
1056
1057 if (!cg->http)
1058 {
1059 if ((cg->http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC,
1060 cupsEncryption(), 1, 30000, NULL)) == NULL)
1061 {
1062 if (errno)
1063 _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, NULL, 0);
1064 else
1065 _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE,
1066 _("Unable to connect to host."), 1);
1067 }
1068 }
1069
1070 /*
1071 * Return the cached connection...
1072 */
1073
1074 return (cg->http);
1075 }
1076
1077
1078 /*
1079 * '_cupsSetError()' - Set the last IPP status code and status-message.
1080 */
1081
1082 void
1083 _cupsSetError(ipp_status_t status, /* I - IPP status code */
1084 const char *message, /* I - status-message value */
1085 int localize) /* I - Localize the message? */
1086 {
1087 _cups_globals_t *cg; /* Global data */
1088
1089
1090 if (!message && errno)
1091 {
1092 message = strerror(errno);
1093 localize = 0;
1094 }
1095
1096 cg = _cupsGlobals();
1097 cg->last_error = status;
1098
1099 if (cg->last_status_message)
1100 {
1101 _cupsStrFree(cg->last_status_message);
1102
1103 cg->last_status_message = NULL;
1104 }
1105
1106 if (message)
1107 {
1108 if (localize)
1109 {
1110 /*
1111 * Get the message catalog...
1112 */
1113
1114 if (!cg->lang_default)
1115 cg->lang_default = cupsLangDefault();
1116
1117 cg->last_status_message = _cupsStrAlloc(_cupsLangString(cg->lang_default,
1118 message));
1119 }
1120 else
1121 cg->last_status_message = _cupsStrAlloc(message);
1122 }
1123
1124 DEBUG_printf(("4_cupsSetError: last_error=%s, last_status_message=\"%s\"",
1125 ippErrorString(cg->last_error), cg->last_status_message));
1126 }
1127
1128
1129 /*
1130 * '_cupsSetHTTPError()' - Set the last error using the HTTP status.
1131 */
1132
1133 void
1134 _cupsSetHTTPError(http_status_t status) /* I - HTTP status code */
1135 {
1136 switch (status)
1137 {
1138 case HTTP_STATUS_NOT_FOUND :
1139 _cupsSetError(IPP_STATUS_ERROR_NOT_FOUND, httpStatus(status), 0);
1140 break;
1141
1142 case HTTP_STATUS_UNAUTHORIZED :
1143 _cupsSetError(IPP_STATUS_ERROR_NOT_AUTHENTICATED, httpStatus(status), 0);
1144 break;
1145
1146 case HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED :
1147 _cupsSetError(IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED, httpStatus(status), 0);
1148 break;
1149
1150 case HTTP_STATUS_FORBIDDEN :
1151 _cupsSetError(IPP_STATUS_ERROR_FORBIDDEN, httpStatus(status), 0);
1152 break;
1153
1154 case HTTP_STATUS_BAD_REQUEST :
1155 _cupsSetError(IPP_STATUS_ERROR_BAD_REQUEST, httpStatus(status), 0);
1156 break;
1157
1158 case HTTP_STATUS_REQUEST_TOO_LARGE :
1159 _cupsSetError(IPP_STATUS_ERROR_REQUEST_VALUE, httpStatus(status), 0);
1160 break;
1161
1162 case HTTP_STATUS_NOT_IMPLEMENTED :
1163 _cupsSetError(IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED, httpStatus(status), 0);
1164 break;
1165
1166 case HTTP_STATUS_NOT_SUPPORTED :
1167 _cupsSetError(IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED, httpStatus(status), 0);
1168 break;
1169
1170 case HTTP_STATUS_UPGRADE_REQUIRED :
1171 _cupsSetError(IPP_STATUS_ERROR_CUPS_UPGRADE_REQUIRED, httpStatus(status), 0);
1172 break;
1173
1174 case HTTP_STATUS_CUPS_PKI_ERROR :
1175 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, httpStatus(status), 0);
1176 break;
1177
1178 case HTTP_STATUS_ERROR :
1179 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
1180 break;
1181
1182 default :
1183 DEBUG_printf(("4_cupsSetHTTPError: HTTP error %d mapped to "
1184 "IPP_STATUS_ERROR_SERVICE_UNAVAILABLE!", status));
1185 _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, httpStatus(status), 0);
1186 break;
1187 }
1188 }
1189
1190
1191 /*
1192 * End of "$Id$".
1193 */