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