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