]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/request.c
Import CUPS 1.4svn-r7356.
[thirdparty/cups.git] / cups / request.c
1 /*
2 * "$Id: request.c 6879 2007-08-29 20:26:50Z mike $"
3 *
4 * IPP utilities for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2007-2008 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products.
8 *
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/".
14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 *
17 * Contents:
18 *
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.
23 * cupsReadResponseData() - Read additional data after the IPP response.
24 * cupsSendRequest() - Send an IPP request.
25 * cupsWriteRequestData() - Write additional data after an IPP request.
26 * _cupsSetError() - Set the last IPP status code and status-message.
27 * _cupsSetHTTPError() - Set the last error using the HTTP status.
28 */
29
30 /*
31 * Include necessary headers...
32 */
33
34 #include "globals.h"
35 #include "debug.h"
36 #include <stdlib.h>
37 #include <errno.h>
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__ */
45 #ifndef O_BINARY
46 # define O_BINARY 0
47 #endif /* O_BINARY */
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
54 * and authenticating as necessary. The request is freed with @link ippDelete@
55 * after receiving a valid IPP response.
56 */
57
58 ipp_t * /* O - Response data */
59 cupsDoFileRequest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
60 ipp_t *request, /* I - IPP request */
61 const char *resource, /* I - HTTP resource for POST */
62 const char *filename) /* I - File to send or @code NULL@ for none */
63 {
64 ipp_t *response; /* IPP response data */
65 int infile; /* Input file */
66
67
68 if (filename)
69 {
70 if ((infile = open(filename, O_RDONLY | O_BINARY)) < 0)
71 {
72 /*
73 * Can't get file information!
74 */
75
76 _cupsSetError(errno == ENOENT ? IPP_NOT_FOUND : IPP_NOT_AUTHORIZED,
77 strerror(errno));
78
79 ippDelete(request);
80
81 return (NULL);
82 }
83 }
84 else
85 infile = -1;
86
87 response = cupsDoIORequest(http, request, resource, infile, -1);
88
89 if (infile >= 0)
90 close(infile);
91
92 return (response);
93 }
94
95
96 /*
97 * 'cupsDoIORequest()' - Do an IPP request with file descriptors.
98 *
99 * This function sends the IPP request to the specified server, retrying
100 * and authenticating as necessary. The request is freed with ippDelete()
101 * after receiving a valid IPP response.
102 *
103 * If "infile" is a valid file descriptor, cupsDoIORequest() copies
104 * all of the data from the file after the IPP request message.
105 *
106 * If "outfile" is a valid file descriptor, cupsDoIORequest() copies
107 * all of the data after the IPP response message to the file.
108 *
109 * @since CUPS 1.3@
110 */
111
112 ipp_t * /* O - Response data */
113 cupsDoIORequest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
114 ipp_t *request, /* I - IPP request */
115 const char *resource, /* I - HTTP resource for POST */
116 int infile, /* I - File to read from or -1 for none */
117 int outfile) /* I - File to write to or -1 for none */
118 {
119 ipp_t *response = NULL; /* IPP response data */
120 size_t length = 0; /* Content-Length value */
121 http_status_t status; /* Status of HTTP request */
122 struct stat fileinfo; /* File information */
123 int bytes; /* Number of bytes read/written */
124 char buffer[32768]; /* Output buffer */
125
126
127 DEBUG_printf(("cupsDoIORequest(http=%p, request=%p, resource=\"%s\""
128 "infile=%d, outfile=%d)\n", http, request,
129 resource ? resource : "(null)", infile, outfile));
130
131 /*
132 * Range check input...
133 */
134
135 if (!request || !resource)
136 {
137 ippDelete(request);
138
139 _cupsSetError(IPP_INTERNAL_ERROR, strerror(EINVAL));
140
141 return (NULL);
142 }
143
144 /*
145 * Get the default connection as needed...
146 */
147
148 if (!http)
149 http = _cupsConnect();
150
151 /*
152 * See if we have a file to send...
153 */
154
155 if (infile >= 0)
156 {
157 if (fstat(infile, &fileinfo))
158 {
159 /*
160 * Can't get file information!
161 */
162
163 _cupsSetError(errno == EBADF ? IPP_NOT_FOUND : IPP_NOT_AUTHORIZED,
164 strerror(errno));
165
166 ippDelete(request);
167
168 return (NULL);
169 }
170
171 #ifdef WIN32
172 if (fileinfo.st_mode & _S_IFDIR)
173 #else
174 if (S_ISDIR(fileinfo.st_mode))
175 #endif /* WIN32 */
176 {
177 /*
178 * Can't send a directory...
179 */
180
181 ippDelete(request);
182
183 _cupsSetError(IPP_NOT_POSSIBLE, strerror(EISDIR));
184
185 return (NULL);
186 }
187
188 #ifndef WIN32
189 if (!S_ISREG(fileinfo.st_mode))
190 length = 0; /* Chunk when piping */
191 else
192 #endif /* !WIN32 */
193 length = ippLength(request) + fileinfo.st_size;
194 }
195 else
196 length = ippLength(request);
197
198 /*
199 * Loop until we can send the request without authorization problems.
200 */
201
202 while (response == NULL)
203 {
204 DEBUG_puts("cupsDoFileRequest: setup...");
205
206 /*
207 * Send the request...
208 */
209
210 status = cupsSendRequest(http, request, resource, length);
211
212 DEBUG_printf(("cupsDoFileRequest: status=%d\n", status));
213
214 if (status == HTTP_CONTINUE && request->state == IPP_DATA && infile >= 0)
215 {
216 DEBUG_puts("cupsDoFileRequest: file write...");
217
218 /*
219 * Send the file with the request...
220 */
221
222 #ifndef WIN32
223 if (S_ISREG(fileinfo.st_mode))
224 #endif /* WIN32 */
225 lseek(infile, 0, SEEK_SET);
226
227 while ((bytes = (int)read(infile, buffer, sizeof(buffer))) > 0)
228 {
229 if (httpCheck(http))
230 {
231 if ((status = httpUpdate(http)) != HTTP_CONTINUE)
232 break;
233 }
234
235 if (httpWrite2(http, buffer, bytes) < bytes)
236 break;
237 }
238 }
239
240 /*
241 * Get the server's response...
242 */
243
244 if (status == HTTP_CONTINUE || status == HTTP_OK)
245 {
246 response = cupsGetResponse(http, resource);
247 status = http->status;
248 }
249
250 if (status == HTTP_FORBIDDEN)
251 break;
252
253 if (response)
254 {
255 if (outfile >= 0)
256 {
257 /*
258 * Write trailing data to file...
259 */
260
261 while ((bytes = (int)httpRead2(http, buffer, sizeof(buffer))) > 0)
262 if (write(outfile, buffer, bytes) < bytes)
263 break;
264 }
265 else
266 {
267 /*
268 * Flush any remaining data...
269 */
270
271 httpFlush(http);
272 }
273 }
274 }
275
276 /*
277 * Delete the original request and return the response...
278 */
279
280 ippDelete(request);
281
282 return (response);
283 }
284
285
286 /*
287 * 'cupsDoRequest()' - Do an IPP request.
288 *
289 * This function sends the IPP request to the specified server, retrying
290 * and authenticating as necessary. The request is freed with ippDelete()
291 * after receiving a valid IPP response.
292 */
293
294 ipp_t * /* O - Response data */
295 cupsDoRequest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
296 ipp_t *request, /* I - IPP request */
297 const char *resource) /* I - HTTP resource for POST */
298 {
299 return (cupsDoFileRequest(http, request, resource, NULL));
300 }
301
302
303 /*
304 * 'cupsGetResponse()' - Get a response to an IPP request.
305 *
306 * Use this function to get the response for an IPP request sent using
307 * cupsSendDocument() or cupsSendRequest(). For requests that return
308 * additional data, use httpRead() after getting a successful response.
309 *
310 * @since CUPS 1.4@
311 */
312
313 ipp_t * /* O - Response or @code NULL@ on HTTP error */
314 cupsGetResponse(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
315 const char *resource) /* I - HTTP resource for POST */
316 {
317 http_status_t status; /* HTTP status */
318 ipp_state_t state; /* IPP read state */
319 ipp_t *response = NULL; /* IPP response */
320
321
322 DEBUG_printf(("cupsGetReponse(http=%p)\n", http));
323
324 /*
325 * Connect to the default server as needed...
326 */
327
328 if (!http)
329 http = _cupsConnect();
330
331 if (!http || (http->state != HTTP_POST_RECV && http->state != HTTP_POST_SEND))
332 return (NULL);
333
334 /*
335 * Check for an unfinished chunked request...
336 */
337
338 if (http->data_encoding == HTTP_ENCODE_CHUNKED)
339 {
340 /*
341 * Send a 0-length chunk to finish off the request...
342 */
343
344 DEBUG_puts("cupsGetResponse: Finishing chunked POST...");
345
346 if (httpWrite2(http, "", 0) < 0)
347 return (NULL);
348 }
349
350 /*
351 * Wait for a response from the server...
352 */
353
354 DEBUG_puts("cupsGetResponse: update...");
355
356 while ((status = httpUpdate(http)) == HTTP_CONTINUE)
357 /* Do nothing but update */;
358
359 DEBUG_printf(("cupsGetResponse: status = %d\n", status));
360
361 if (status == HTTP_OK)
362 {
363 /*
364 * Get the IPP response...
365 */
366
367 response = ippNew();
368
369 while ((state = ippRead(http, response)) != IPP_DATA)
370 if (state == IPP_ERROR)
371 break;
372
373 if (state == IPP_ERROR)
374 {
375 /*
376 * Delete the response...
377 */
378
379 DEBUG_puts("IPP read error!");
380
381 ippDelete(response);
382 response = NULL;
383
384 _cupsSetError(IPP_SERVICE_UNAVAILABLE, strerror(errno));
385 }
386 }
387 else if (status != HTTP_ERROR)
388 {
389 /*
390 * Flush any error message...
391 */
392
393 httpFlush(http);
394
395 /*
396 * Then handle encryption and authentication...
397 */
398
399 if (status == HTTP_UNAUTHORIZED)
400 {
401 /*
402 * See if we can do authentication...
403 */
404
405 int auth_result;
406
407 DEBUG_puts("cupsGetResponse: Need authorization...");
408
409 if ((auth_result =cupsDoAuthentication(http, "POST", resource)) == 0)
410 httpReconnect(http);
411 else if (auth_result < 0)
412 http->status = status = HTTP_FORBIDDEN;
413 }
414
415 #ifdef HAVE_SSL
416 else if (status == HTTP_UPGRADE_REQUIRED)
417 {
418 /*
419 * Force a reconnect with encryption...
420 */
421
422 DEBUG_puts("cupsGetResponse: Need encryption...");
423
424 if (!httpReconnect(http))
425 httpEncryption(http, HTTP_ENCRYPT_REQUIRED);
426 }
427 #endif /* HAVE_SSL */
428 }
429
430 if (response)
431 {
432 ipp_attribute_t *attr; /* status-message attribute */
433
434
435 attr = ippFindAttribute(response, "status-message", IPP_TAG_TEXT);
436
437 _cupsSetError(response->request.status.status_code,
438 attr ? attr->values[0].string.text :
439 ippErrorString(response->request.status.status_code));
440 }
441 else if (status != HTTP_OK)
442 _cupsSetHTTPError(status);
443
444 return (response);
445 }
446
447
448 /*
449 * 'cupsReadResponseData()' - Read additional data after the IPP response.
450 *
451 * This function is used after cupsGetResponse() to read the PPD or document
452 * files for CUPS_GET_PPD and CUPS_GET_DOCUMENT requests, respectively.
453 *
454 * @since CUPS 1.4@
455 */
456
457 ssize_t /* O - Bytes read, 0 on EOF, -1 on error */
458 cupsReadResponseData(
459 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
460 char *buffer, /* I - Buffer to use */
461 size_t length) /* I - Number of bytes to read */
462 {
463 /*
464 * Get the default connection as needed...
465 */
466
467 if (!http)
468 {
469 _cups_globals_t *cg = _cupsGlobals();
470 /* Pointer to library globals */
471
472 if ((http = cg->http) == NULL)
473 {
474 _cupsSetError(IPP_INTERNAL_ERROR, "No active connection");
475 return (-1);
476 }
477 }
478
479 /*
480 * Then read from the HTTP connection...
481 */
482
483 return (httpRead2(http, buffer, length));
484 }
485
486
487 /*
488 * 'cupsSendRequest()' - Send an IPP request.
489 *
490 * Use httpWrite() to write any additional data (document, PPD file, etc.)
491 * for the request, cupsGetResponse() to get the IPP response, and httpRead()
492 * to read any additional data following the response. Only one request can be
493 * sent/queued at a time.
494 *
495 * Unlike cupsDoFileRequest(), cupsDoIORequest(), and cupsDoRequest(), the
496 * request is not freed.
497 *
498 * @since CUPS 1.4@
499 */
500
501 http_status_t /* O - Initial HTTP status */
502 cupsSendRequest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
503 ipp_t *request, /* I - IPP request */
504 const char *resource, /* I - Resource path */
505 size_t length) /* I - Length of data to follow or CUPS_LENGTH_VARIABLE */
506 {
507 http_status_t status; /* Status of HTTP request */
508 int got_status; /* Did we get the status? */
509 ipp_state_t state; /* State of IPP processing */
510 http_status_t expect; /* Expect: header to use */
511
512
513 DEBUG_printf(("cupsSendRequest(http=%p, request=%p, resource=\"%s\", "
514 "length=" CUPS_LLFMT ")\n", http, request,
515 resource ? resource : "(null)", CUPS_LLCAST length));
516
517 /*
518 * Range check input...
519 */
520
521 if (!request || !resource)
522 {
523 _cupsSetError(IPP_INTERNAL_ERROR, strerror(EINVAL));
524
525 return (HTTP_ERROR);
526 }
527
528 /*
529 * Get the default connection as needed...
530 */
531
532 if (!http)
533 http = _cupsConnect();
534
535 #ifdef HAVE_SSL
536 /*
537 * See if we have an auth-info attribute and are communicating over
538 * a non-local link. If so, encrypt the link so that we can pass
539 * the authentication information securely...
540 */
541
542 if (ippFindAttribute(request, "auth-info", IPP_TAG_TEXT) &&
543 !httpAddrLocalhost(http->hostaddr) && !http->tls &&
544 httpEncryption(http, HTTP_ENCRYPT_REQUIRED))
545 return (HTTP_ERROR);
546 #endif /* HAVE_SSL */
547
548 /*
549 * Loop until we can send the request without authorization problems.
550 */
551
552 status = HTTP_ERROR;
553 expect = HTTP_CONTINUE;
554
555 for (;;)
556 {
557 DEBUG_puts("cupsSendRequest: setup...");
558
559 /*
560 * Setup the HTTP variables needed...
561 */
562
563 httpClearFields(http);
564 httpSetLength(http, length);
565 httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
566 httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring);
567 httpSetExpect(http, expect);
568
569 DEBUG_printf(("cupsSendRequest: authstring=\"%s\"\n", http->authstring));
570
571 /*
572 * Try the request...
573 */
574
575 DEBUG_puts("cupsSendRequest: post...");
576
577 if (httpPost(http, resource))
578 {
579 if (httpReconnect(http))
580 return (HTTP_ERROR);
581 else
582 continue;
583 }
584
585 /*
586 * Send the IPP data...
587 */
588
589 DEBUG_puts("cupsSendRequest: ipp write...");
590
591 request->state = IPP_IDLE;
592 status = HTTP_CONTINUE;
593 got_status = 0;
594
595 while ((state = ippWrite(http, request)) != IPP_DATA)
596 if (state == IPP_ERROR)
597 break;
598 else if (httpCheck(http))
599 {
600 got_status = 1;
601
602 if ((status = httpUpdate(http)) != HTTP_CONTINUE)
603 break;
604 }
605
606 /*
607 * Wait up to 1 second to get the 100-continue response as needed...
608 */
609
610 if (!got_status && expect == HTTP_CONTINUE)
611 {
612 if (httpWait(http, 1000))
613 status = httpUpdate(http);
614 }
615 else if (httpCheck(http))
616 status = httpUpdate(http);
617
618 /*
619 * Process the current HTTP status...
620 */
621
622 switch (status)
623 {
624 case HTTP_ERROR :
625 case HTTP_CONTINUE :
626 case HTTP_OK :
627 return (status);
628
629 case HTTP_UNAUTHORIZED :
630 if (!cupsDoAuthentication(http, "POST", resource))
631 if (httpReconnect(http))
632 return (HTTP_ERROR);
633
634 return (status);
635
636 #ifdef HAVE_SSL
637 case HTTP_UPGRADE_REQUIRED :
638 /*
639 * Flush any error message, reconnect, and then upgrade with
640 * encryption...
641 */
642
643 if (httpReconnect(http))
644 return (HTTP_ERROR);
645
646 httpEncryption(http, HTTP_ENCRYPT_REQUIRED);
647
648 return (status);
649 #endif /* HAVE_SSL */
650
651 case HTTP_EXPECTATION_FAILED :
652 /*
653 * Don't try using the Expect: header the next time around...
654 */
655
656 expect = (http_status_t)0;
657
658 default :
659 /*
660 * Some other error...
661 */
662
663 return (status);
664 }
665 }
666 }
667
668
669 /*
670 * 'cupsWriteRequestData()' - Write additional data after an IPP request.
671 *
672 * This function is used after @link cupsSendRequest@ to provide a PPD and
673 * after @link cupsStartDocument@ to provide a document file.
674 *
675 * @since CUPS 1.4@
676 */
677
678 http_status_t /* O - @code HTTP_CONTINUE@ if OK or HTTP status on error */
679 cupsWriteRequestData(
680 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
681 const char *buffer, /* I - Bytes to write */
682 size_t length) /* I - Number of bytes to write */
683 {
684 /*
685 * Get the default connection as needed...
686 */
687
688 if (!http)
689 {
690 _cups_globals_t *cg = _cupsGlobals();
691 /* Pointer to library globals */
692
693 if ((http = cg->http) == NULL)
694 {
695 _cupsSetError(IPP_INTERNAL_ERROR, "No active connection");
696 return (HTTP_ERROR);
697 }
698 }
699
700 /*
701 * Then write to the HTTP connection...
702 */
703
704 if (httpWrite2(http, buffer, length) < 0)
705 return (HTTP_ERROR);
706
707 /*
708 * Finally, check if we have any pending data from the server...
709 */
710
711 if (httpCheck(http))
712 return (httpUpdate(http));
713 else
714 return (HTTP_CONTINUE);
715 }
716
717
718 /*
719 * '_cupsSetError()' - Set the last IPP status code and status-message.
720 */
721
722 void
723 _cupsSetError(ipp_status_t status, /* I - IPP status code */
724 const char *message) /* I - status-message value */
725 {
726 _cups_globals_t *cg; /* Global data */
727
728
729 cg = _cupsGlobals();
730 cg->last_error = status;
731
732 if (cg->last_status_message)
733 {
734 free(cg->last_status_message);
735
736 cg->last_status_message = NULL;
737 }
738
739 if (message)
740 cg->last_status_message = strdup(message);
741 }
742
743
744 /*
745 * '_cupsSetHTTPError()' - Set the last error using the HTTP status.
746 */
747
748 void
749 _cupsSetHTTPError(http_status_t status) /* I - HTTP status code */
750 {
751 switch (status)
752 {
753 case HTTP_NOT_FOUND :
754 _cupsSetError(IPP_NOT_FOUND, httpStatus(status));
755 break;
756
757 case HTTP_UNAUTHORIZED :
758 _cupsSetError(IPP_NOT_AUTHORIZED, httpStatus(status));
759 break;
760
761 case HTTP_FORBIDDEN :
762 _cupsSetError(IPP_FORBIDDEN, httpStatus(status));
763 break;
764
765 case HTTP_BAD_REQUEST :
766 _cupsSetError(IPP_BAD_REQUEST, httpStatus(status));
767 break;
768
769 case HTTP_REQUEST_TOO_LARGE :
770 _cupsSetError(IPP_REQUEST_VALUE, httpStatus(status));
771 break;
772
773 case HTTP_NOT_IMPLEMENTED :
774 _cupsSetError(IPP_OPERATION_NOT_SUPPORTED, httpStatus(status));
775 break;
776
777 case HTTP_NOT_SUPPORTED :
778 _cupsSetError(IPP_VERSION_NOT_SUPPORTED, httpStatus(status));
779 break;
780
781 default :
782 DEBUG_printf(("HTTP error %d mapped to IPP_SERVICE_UNAVAILABLE!\n",
783 status));
784 _cupsSetError(IPP_SERVICE_UNAVAILABLE, httpStatus(status));
785 break;
786 }
787 }
788
789
790 /*
791 * End of "$Id: request.c 6879 2007-08-29 20:26:50Z mike $".
792 */