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