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