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