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