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