/src/cups/cups/getputfile.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Get/put file functions for CUPS. |
3 | | * |
4 | | * Copyright © 2020-2025 by OpenPrinting. |
5 | | * Copyright © 2007-2018 by Apple Inc. |
6 | | * Copyright © 1997-2006 by Easy Software Products. |
7 | | * |
8 | | * Licensed under Apache License v2.0. See the file "LICENSE" for more |
9 | | * information. |
10 | | */ |
11 | | |
12 | | #include "cups-private.h" |
13 | | #include "debug-internal.h" |
14 | | #include <fcntl.h> |
15 | | #include <sys/stat.h> |
16 | | #if defined(_WIN32) || defined(__EMX__) |
17 | | # include <io.h> |
18 | | #else |
19 | | # include <unistd.h> |
20 | | #endif /* _WIN32 || __EMX__ */ |
21 | | |
22 | | |
23 | | /* |
24 | | * 'cupsGetFd()' - Get a file from the server. |
25 | | * |
26 | | * This function returns @code HTTP_STATUS_OK@ when the file is successfully retrieved. |
27 | | * |
28 | | * @since CUPS 1.1.20@ |
29 | | */ |
30 | | |
31 | | http_status_t /* O - HTTP status */ |
32 | | cupsGetFd(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ |
33 | | const char *resource, /* I - Resource name */ |
34 | | int fd) /* I - File descriptor */ |
35 | 0 | { |
36 | 0 | ssize_t bytes; /* Number of bytes read */ |
37 | 0 | char buffer[8192]; /* Buffer for file */ |
38 | 0 | http_status_t status; /* HTTP status from server */ |
39 | 0 | char if_modified_since[HTTP_MAX_VALUE]; |
40 | | /* If-Modified-Since header */ |
41 | 0 | int new_auth = 0; /* Using new auth information? */ |
42 | 0 | int digest; /* Are we using Digest authentication? */ |
43 | | |
44 | | |
45 | | /* |
46 | | * Range check input... |
47 | | */ |
48 | |
|
49 | 0 | DEBUG_printf("cupsGetFd(http=%p, resource=\"%s\", fd=%d)", (void *)http, resource, fd); |
50 | |
|
51 | 0 | if (!resource || fd < 0) |
52 | 0 | { |
53 | 0 | if (http) |
54 | 0 | http->error = EINVAL; |
55 | |
|
56 | 0 | return (HTTP_STATUS_ERROR); |
57 | 0 | } |
58 | | |
59 | 0 | if (!http) |
60 | 0 | if ((http = _cupsConnect()) == NULL) |
61 | 0 | return (HTTP_STATUS_SERVICE_UNAVAILABLE); |
62 | | |
63 | | /* |
64 | | * Then send GET requests to the HTTP server... |
65 | | */ |
66 | | |
67 | 0 | cupsCopyString(if_modified_since, httpGetField(http, HTTP_FIELD_IF_MODIFIED_SINCE), |
68 | 0 | sizeof(if_modified_since)); |
69 | |
|
70 | 0 | do |
71 | 0 | { |
72 | 0 | if (!_cups_strcasecmp(httpGetField(http, HTTP_FIELD_CONNECTION), "close")) |
73 | 0 | { |
74 | 0 | httpClearFields(http); |
75 | 0 | if (httpReconnect2(http, 30000, NULL)) |
76 | 0 | { |
77 | 0 | status = HTTP_STATUS_ERROR; |
78 | 0 | break; |
79 | 0 | } |
80 | 0 | } |
81 | | |
82 | 0 | httpClearFields(http); |
83 | 0 | httpSetField(http, HTTP_FIELD_IF_MODIFIED_SINCE, if_modified_since); |
84 | |
|
85 | 0 | digest = http->authstring && !strncmp(http->authstring, "Digest ", 7); |
86 | |
|
87 | 0 | if (digest && !new_auth) |
88 | 0 | { |
89 | | /* |
90 | | * Update the Digest authentication string... |
91 | | */ |
92 | |
|
93 | 0 | _httpSetDigestAuthString(http, http->nextnonce, "GET", resource); |
94 | 0 | } |
95 | |
|
96 | | #ifdef HAVE_GSSAPI |
97 | | if (http->authstring && !strncmp(http->authstring, "Negotiate", 9) && !new_auth) |
98 | | { |
99 | | /* |
100 | | * Do not use cached Kerberos credentials since they will look like a |
101 | | * "replay" attack... |
102 | | */ |
103 | | |
104 | | _cupsSetNegotiateAuthString(http, "GET", resource); |
105 | | } |
106 | | #endif /* HAVE_GSSAPI */ |
107 | |
|
108 | 0 | httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring); |
109 | |
|
110 | 0 | if (httpGet(http, resource)) |
111 | 0 | { |
112 | 0 | if (httpReconnect2(http, 30000, NULL)) |
113 | 0 | { |
114 | 0 | status = HTTP_STATUS_ERROR; |
115 | 0 | break; |
116 | 0 | } |
117 | 0 | else |
118 | 0 | { |
119 | 0 | status = HTTP_STATUS_UNAUTHORIZED; |
120 | 0 | continue; |
121 | 0 | } |
122 | 0 | } |
123 | | |
124 | 0 | new_auth = 0; |
125 | |
|
126 | 0 | while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE); |
127 | |
|
128 | 0 | if (status == HTTP_STATUS_UNAUTHORIZED) |
129 | 0 | { |
130 | | /* |
131 | | * Flush any error message... |
132 | | */ |
133 | |
|
134 | 0 | httpFlush(http); |
135 | | |
136 | | /* |
137 | | * See if we can do authentication... |
138 | | */ |
139 | |
|
140 | 0 | new_auth = 1; |
141 | |
|
142 | 0 | if (cupsDoAuthentication(http, "GET", resource)) |
143 | 0 | { |
144 | 0 | status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED; |
145 | 0 | break; |
146 | 0 | } |
147 | | |
148 | 0 | if (httpReconnect2(http, 30000, NULL)) |
149 | 0 | { |
150 | 0 | status = HTTP_STATUS_ERROR; |
151 | 0 | break; |
152 | 0 | } |
153 | | |
154 | 0 | continue; |
155 | 0 | } |
156 | 0 | else if (status == HTTP_STATUS_UPGRADE_REQUIRED) |
157 | 0 | { |
158 | | /* Flush any error message... */ |
159 | 0 | httpFlush(http); |
160 | | |
161 | | /* Reconnect... */ |
162 | 0 | if (httpReconnect2(http, 30000, NULL)) |
163 | 0 | { |
164 | 0 | status = HTTP_STATUS_ERROR; |
165 | 0 | break; |
166 | 0 | } |
167 | | |
168 | | /* Upgrade with encryption... */ |
169 | 0 | httpEncryption(http, HTTP_ENCRYPTION_REQUIRED); |
170 | | |
171 | | /* Try again, this time with encryption enabled... */ |
172 | 0 | continue; |
173 | 0 | } |
174 | 0 | } |
175 | 0 | while (status == HTTP_STATUS_UNAUTHORIZED || status == HTTP_STATUS_UPGRADE_REQUIRED); |
176 | | |
177 | | /* |
178 | | * See if we actually got the file or an error... |
179 | | */ |
180 | | |
181 | 0 | if (status == HTTP_STATUS_OK) |
182 | 0 | { |
183 | | /* |
184 | | * Yes, copy the file... |
185 | | */ |
186 | |
|
187 | 0 | while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0) |
188 | 0 | write(fd, buffer, (size_t)bytes); |
189 | 0 | } |
190 | 0 | else |
191 | 0 | { |
192 | 0 | _cupsSetHTTPError(http, status); |
193 | 0 | httpFlush(http); |
194 | 0 | } |
195 | | |
196 | | /* |
197 | | * Return the request status... |
198 | | */ |
199 | |
|
200 | 0 | DEBUG_printf("1cupsGetFd: Returning %d...", status); |
201 | |
|
202 | 0 | return (status); |
203 | 0 | } |
204 | | |
205 | | |
206 | | /* |
207 | | * 'cupsGetFile()' - Get a file from the server. |
208 | | * |
209 | | * This function returns @code HTTP_STATUS_OK@ when the file is successfully retrieved. |
210 | | * |
211 | | * @since CUPS 1.1.20@ |
212 | | */ |
213 | | |
214 | | http_status_t /* O - HTTP status */ |
215 | | cupsGetFile(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ |
216 | | const char *resource, /* I - Resource name */ |
217 | | const char *filename) /* I - Filename */ |
218 | 0 | { |
219 | 0 | int fd; /* File descriptor */ |
220 | 0 | http_status_t status; /* Status */ |
221 | | |
222 | | |
223 | | /* |
224 | | * Range check input... |
225 | | */ |
226 | |
|
227 | 0 | if (!http || !resource || !filename) |
228 | 0 | { |
229 | 0 | if (http) |
230 | 0 | http->error = EINVAL; |
231 | |
|
232 | 0 | return (HTTP_STATUS_ERROR); |
233 | 0 | } |
234 | | |
235 | | /* |
236 | | * Create the file... |
237 | | */ |
238 | | |
239 | 0 | if ((fd = open(filename, O_WRONLY | O_EXCL | O_TRUNC)) < 0) |
240 | 0 | { |
241 | | /* |
242 | | * Couldn't open the file! |
243 | | */ |
244 | |
|
245 | 0 | http->error = errno; |
246 | |
|
247 | 0 | return (HTTP_STATUS_ERROR); |
248 | 0 | } |
249 | | |
250 | | /* |
251 | | * Get the file... |
252 | | */ |
253 | | |
254 | 0 | status = cupsGetFd(http, resource, fd); |
255 | | |
256 | | /* |
257 | | * If the file couldn't be gotten, then remove the file... |
258 | | */ |
259 | |
|
260 | 0 | close(fd); |
261 | |
|
262 | 0 | if (status != HTTP_STATUS_OK) |
263 | 0 | unlink(filename); |
264 | | |
265 | | /* |
266 | | * Return the HTTP status code... |
267 | | */ |
268 | |
|
269 | 0 | return (status); |
270 | 0 | } |
271 | | |
272 | | |
273 | | /* |
274 | | * 'cupsPutFd()' - Put a file on the server. |
275 | | * |
276 | | * This function returns @code HTTP_STATUS_CREATED@ when the file is stored |
277 | | * successfully. |
278 | | * |
279 | | * @since CUPS 1.1.20@ |
280 | | */ |
281 | | |
282 | | http_status_t /* O - HTTP status */ |
283 | | cupsPutFd(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ |
284 | | const char *resource, /* I - Resource name */ |
285 | | int fd) /* I - File descriptor */ |
286 | 0 | { |
287 | 0 | ssize_t bytes; /* Number of bytes read */ |
288 | 0 | int retries; /* Number of retries */ |
289 | 0 | char buffer[8192]; /* Buffer for file */ |
290 | 0 | http_status_t status; /* HTTP status from server */ |
291 | 0 | int new_auth = 0; /* Using new auth information? */ |
292 | 0 | int digest; /* Are we using Digest authentication? */ |
293 | | |
294 | | |
295 | | /* |
296 | | * Range check input... |
297 | | */ |
298 | |
|
299 | 0 | DEBUG_printf("cupsPutFd(http=%p, resource=\"%s\", fd=%d)", (void *)http, resource, fd); |
300 | |
|
301 | 0 | if (!resource || fd < 0) |
302 | 0 | { |
303 | 0 | if (http) |
304 | 0 | http->error = EINVAL; |
305 | |
|
306 | 0 | return (HTTP_STATUS_ERROR); |
307 | 0 | } |
308 | | |
309 | 0 | if (!http) |
310 | 0 | if ((http = _cupsConnect()) == NULL) |
311 | 0 | return (HTTP_STATUS_SERVICE_UNAVAILABLE); |
312 | | |
313 | | /* |
314 | | * Then send PUT requests to the HTTP server... |
315 | | */ |
316 | | |
317 | 0 | retries = 0; |
318 | |
|
319 | 0 | do |
320 | 0 | { |
321 | 0 | if (!_cups_strcasecmp(httpGetField(http, HTTP_FIELD_CONNECTION), "close")) |
322 | 0 | { |
323 | 0 | httpClearFields(http); |
324 | 0 | if (httpReconnect2(http, 30000, NULL)) |
325 | 0 | { |
326 | 0 | status = HTTP_STATUS_ERROR; |
327 | 0 | break; |
328 | 0 | } |
329 | 0 | } |
330 | | |
331 | 0 | DEBUG_printf("2cupsPutFd: starting attempt, authstring=\"%s\"...", http->authstring); |
332 | |
|
333 | 0 | httpClearFields(http); |
334 | 0 | httpSetField(http, HTTP_FIELD_TRANSFER_ENCODING, "chunked"); |
335 | 0 | httpSetExpect(http, HTTP_STATUS_CONTINUE); |
336 | |
|
337 | 0 | digest = http->authstring && !strncmp(http->authstring, "Digest ", 7); |
338 | |
|
339 | 0 | if (digest && !new_auth) |
340 | 0 | { |
341 | | /* |
342 | | * Update the Digest authentication string... |
343 | | */ |
344 | |
|
345 | 0 | _httpSetDigestAuthString(http, http->nextnonce, "PUT", resource); |
346 | 0 | } |
347 | |
|
348 | | #ifdef HAVE_GSSAPI |
349 | | if (http->authstring && !strncmp(http->authstring, "Negotiate", 9) && !new_auth) |
350 | | { |
351 | | /* |
352 | | * Do not use cached Kerberos credentials since they will look like a |
353 | | * "replay" attack... |
354 | | */ |
355 | | |
356 | | _cupsSetNegotiateAuthString(http, "PUT", resource); |
357 | | } |
358 | | #endif /* HAVE_GSSAPI */ |
359 | |
|
360 | 0 | httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring); |
361 | |
|
362 | 0 | if (httpPut(http, resource)) |
363 | 0 | { |
364 | 0 | if (httpReconnect2(http, 30000, NULL)) |
365 | 0 | { |
366 | 0 | status = HTTP_STATUS_ERROR; |
367 | 0 | break; |
368 | 0 | } |
369 | 0 | else |
370 | 0 | { |
371 | 0 | status = HTTP_STATUS_UNAUTHORIZED; |
372 | 0 | continue; |
373 | 0 | } |
374 | 0 | } |
375 | | |
376 | | /* |
377 | | * Wait up to 1 second for a 100-continue response... |
378 | | */ |
379 | | |
380 | 0 | if (httpWait(http, 1000)) |
381 | 0 | status = httpUpdate(http); |
382 | 0 | else |
383 | 0 | status = HTTP_STATUS_CONTINUE; |
384 | |
|
385 | 0 | if (status == HTTP_STATUS_CONTINUE) |
386 | 0 | { |
387 | | /* |
388 | | * Copy the file... |
389 | | */ |
390 | |
|
391 | 0 | lseek(fd, 0, SEEK_SET); |
392 | |
|
393 | 0 | while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) |
394 | 0 | if (httpCheck(http)) |
395 | 0 | { |
396 | 0 | if ((status = httpUpdate(http)) != HTTP_STATUS_CONTINUE) |
397 | 0 | break; |
398 | 0 | } |
399 | 0 | else |
400 | 0 | httpWrite2(http, buffer, (size_t)bytes); |
401 | 0 | } |
402 | |
|
403 | 0 | if (status == HTTP_STATUS_CONTINUE) |
404 | 0 | { |
405 | 0 | httpWrite2(http, buffer, 0); |
406 | |
|
407 | 0 | while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE); |
408 | 0 | } |
409 | |
|
410 | 0 | if (status == HTTP_STATUS_ERROR && !retries) |
411 | 0 | { |
412 | 0 | DEBUG_printf("2cupsPutFd: retry on status %d", status); |
413 | |
|
414 | 0 | retries ++; |
415 | 0 | status = HTTP_STATUS_NONE; |
416 | | |
417 | | /* Flush any error message... */ |
418 | 0 | httpFlush(http); |
419 | | |
420 | | /* Reconnect... */ |
421 | 0 | if (httpReconnect2(http, 30000, NULL)) |
422 | 0 | { |
423 | 0 | status = HTTP_STATUS_ERROR; |
424 | 0 | break; |
425 | 0 | } |
426 | | |
427 | | /* Try again... */ |
428 | 0 | continue; |
429 | 0 | } |
430 | | |
431 | 0 | DEBUG_printf("2cupsPutFd: status=%d", status); |
432 | |
|
433 | 0 | new_auth = 0; |
434 | |
|
435 | 0 | if (status == HTTP_STATUS_UNAUTHORIZED) |
436 | 0 | { |
437 | | /* |
438 | | * Flush any error message... |
439 | | */ |
440 | |
|
441 | 0 | httpFlush(http); |
442 | | |
443 | | /* |
444 | | * See if we can do authentication... |
445 | | */ |
446 | |
|
447 | 0 | new_auth = 1; |
448 | |
|
449 | 0 | if (cupsDoAuthentication(http, "PUT", resource)) |
450 | 0 | { |
451 | 0 | status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED; |
452 | 0 | break; |
453 | 0 | } |
454 | | |
455 | 0 | if (httpReconnect2(http, 30000, NULL)) |
456 | 0 | { |
457 | 0 | status = HTTP_STATUS_ERROR; |
458 | 0 | break; |
459 | 0 | } |
460 | | |
461 | 0 | continue; |
462 | 0 | } |
463 | 0 | else if (status == HTTP_STATUS_UPGRADE_REQUIRED) |
464 | 0 | { |
465 | | /* Flush any error message... */ |
466 | 0 | httpFlush(http); |
467 | | |
468 | | /* Reconnect... */ |
469 | 0 | if (httpReconnect2(http, 30000, NULL)) |
470 | 0 | { |
471 | 0 | status = HTTP_STATUS_ERROR; |
472 | 0 | break; |
473 | 0 | } |
474 | | |
475 | | /* Upgrade with encryption... */ |
476 | 0 | httpEncryption(http, HTTP_ENCRYPTION_REQUIRED); |
477 | | |
478 | | /* Try again, this time with encryption enabled... */ |
479 | 0 | continue; |
480 | 0 | } |
481 | 0 | } |
482 | 0 | while (status == HTTP_STATUS_UNAUTHORIZED || status == HTTP_STATUS_UPGRADE_REQUIRED || status == HTTP_STATUS_NONE); |
483 | | |
484 | | /* |
485 | | * See if we actually put the file or an error... |
486 | | */ |
487 | | |
488 | 0 | if (status != HTTP_STATUS_CREATED) |
489 | 0 | { |
490 | 0 | _cupsSetHTTPError(http, status); |
491 | 0 | httpFlush(http); |
492 | 0 | } |
493 | |
|
494 | 0 | DEBUG_printf("1cupsPutFd: Returning %d...", status); |
495 | |
|
496 | 0 | return (status); |
497 | 0 | } |
498 | | |
499 | | |
500 | | /* |
501 | | * 'cupsPutFile()' - Put a file on the server. |
502 | | * |
503 | | * This function returns @code HTTP_CREATED@ when the file is stored |
504 | | * successfully. |
505 | | * |
506 | | * @since CUPS 1.1.20@ |
507 | | */ |
508 | | |
509 | | http_status_t /* O - HTTP status */ |
510 | | cupsPutFile(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ |
511 | | const char *resource, /* I - Resource name */ |
512 | | const char *filename) /* I - Filename */ |
513 | 0 | { |
514 | 0 | int fd; /* File descriptor */ |
515 | 0 | http_status_t status; /* Status */ |
516 | | |
517 | | |
518 | | /* |
519 | | * Range check input... |
520 | | */ |
521 | |
|
522 | 0 | if (!http || !resource || !filename) |
523 | 0 | { |
524 | 0 | if (http) |
525 | 0 | http->error = EINVAL; |
526 | |
|
527 | 0 | return (HTTP_STATUS_ERROR); |
528 | 0 | } |
529 | | |
530 | | /* |
531 | | * Open the local file... |
532 | | */ |
533 | | |
534 | 0 | if ((fd = open(filename, O_RDONLY)) < 0) |
535 | 0 | { |
536 | | /* |
537 | | * Couldn't open the file! |
538 | | */ |
539 | |
|
540 | 0 | http->error = errno; |
541 | |
|
542 | 0 | return (HTTP_STATUS_ERROR); |
543 | 0 | } |
544 | | |
545 | | /* |
546 | | * Put the file... |
547 | | */ |
548 | | |
549 | 0 | status = cupsPutFd(http, resource, fd); |
550 | |
|
551 | 0 | close(fd); |
552 | |
|
553 | 0 | return (status); |
554 | 0 | } |