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