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