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