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