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