]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/getputfile.c
Migrate Windows conditional code to _WIN32 define.
[thirdparty/cups.git] / cups / getputfile.c
CommitLineData
ef416fc2 1/*
7e86f2f6 2 * Get/put file functions for CUPS.
ef416fc2 3 *
16f67389 4 * Copyright 2007-2018 by Apple Inc.
7e86f2f6 5 * Copyright 1997-2006 by Easy Software Products.
ef416fc2 6 *
16f67389
MS
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
8 * information.
ef416fc2 9 */
10
11/*
12 * Include necessary headers...
13 */
14
71e16022 15#include "cups-private.h"
ef416fc2 16#include <fcntl.h>
17#include <sys/stat.h>
19dc16f7 18#if defined(_WIN32) || defined(__EMX__)
ef416fc2 19# include <io.h>
20#else
21# include <unistd.h>
19dc16f7 22#endif /* _WIN32 || __EMX__ */
ef416fc2 23
24
25/*
26 * 'cupsGetFd()' - Get a file from the server.
27 *
cb7f98ee 28 * This function returns @code HTTP_STATUS_OK@ when the file is successfully retrieved.
ef416fc2 29 *
8072030b 30 * @since CUPS 1.1.20/macOS 10.4@
ef416fc2 31 */
32
ecdc0628 33http_status_t /* O - HTTP status */
568fa3fa 34cupsGetFd(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
ef416fc2 35 const char *resource, /* I - Resource name */
36 int fd) /* I - File descriptor */
37{
7e86f2f6 38 ssize_t bytes; /* Number of bytes read */
ef416fc2 39 char buffer[8192]; /* Buffer for file */
40 http_status_t status; /* HTTP status from server */
757d2cad 41 char if_modified_since[HTTP_MAX_VALUE];
42 /* If-Modified-Since header */
16f67389
MS
43 int new_auth = 0; /* Using new auth information? */
44 int digest; /* Are we using Digest authentication? */
ef416fc2 45
46
47 /*
48 * Range check input...
49 */
50
807315e6 51 DEBUG_printf(("cupsGetFd(http=%p, resource=\"%s\", fd=%d)", (void *)http, resource, fd));
ef416fc2 52
5a738aea 53 if (!resource || fd < 0)
ef416fc2 54 {
55 if (http)
56 http->error = EINVAL;
57
cb7f98ee 58 return (HTTP_STATUS_ERROR);
ef416fc2 59 }
60
5a738aea 61 if (!http)
a603edef 62 if ((http = _cupsConnect()) == NULL)
cb7f98ee 63 return (HTTP_STATUS_SERVICE_UNAVAILABLE);
5a738aea 64
ef416fc2 65 /*
66 * Then send GET requests to the HTTP server...
67 */
68
757d2cad 69 strlcpy(if_modified_since, httpGetField(http, HTTP_FIELD_IF_MODIFIED_SINCE),
70 sizeof(if_modified_since));
71
ef416fc2 72 do
73 {
0c4bedc4
MS
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
ef416fc2 84 httpClearFields(http);
757d2cad 85 httpSetField(http, HTTP_FIELD_IF_MODIFIED_SINCE, if_modified_since);
ef416fc2 86
16f67389
MS
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
5737d5eb 95 _httpSetDigestAuthString(http, http->nextnonce, "GET", resource);
16f67389
MS
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
ef416fc2 112 if (httpGet(http, resource))
113 {
cb7f98ee 114 if (httpReconnect2(http, 30000, NULL))
ef416fc2 115 {
cb7f98ee 116 status = HTTP_STATUS_ERROR;
ef416fc2 117 break;
118 }
119 else
120 {
cb7f98ee 121 status = HTTP_STATUS_UNAUTHORIZED;
ef416fc2 122 continue;
123 }
124 }
125
16f67389
MS
126 new_auth = 0;
127
cb7f98ee 128 while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
ef416fc2 129
cb7f98ee 130 if (status == HTTP_STATUS_UNAUTHORIZED)
ef416fc2 131 {
132 /*
133 * Flush any error message...
134 */
135
136 httpFlush(http);
137
138 /*
139 * See if we can do authentication...
140 */
141
16f67389
MS
142 new_auth = 1;
143
ef416fc2 144 if (cupsDoAuthentication(http, "GET", resource))
f11a948a 145 {
cb7f98ee 146 status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
ef416fc2 147 break;
f11a948a 148 }
ef416fc2 149
cb7f98ee 150 if (httpReconnect2(http, 30000, NULL))
fa73b229 151 {
cb7f98ee 152 status = HTTP_STATUS_ERROR;
fa73b229 153 break;
154 }
ef416fc2 155
156 continue;
157 }
158#ifdef HAVE_SSL
cb7f98ee 159 else if (status == HTTP_STATUS_UPGRADE_REQUIRED)
ef416fc2 160 {
161 /* Flush any error message... */
162 httpFlush(http);
163
164 /* Reconnect... */
cb7f98ee 165 if (httpReconnect2(http, 30000, NULL))
fa73b229 166 {
cb7f98ee 167 status = HTTP_STATUS_ERROR;
fa73b229 168 break;
169 }
ef416fc2 170
171 /* Upgrade with encryption... */
cb7f98ee 172 httpEncryption(http, HTTP_ENCRYPTION_REQUIRED);
ef416fc2 173
174 /* Try again, this time with encryption enabled... */
175 continue;
176 }
177#endif /* HAVE_SSL */
178 }
cb7f98ee 179 while (status == HTTP_STATUS_UNAUTHORIZED || status == HTTP_STATUS_UPGRADE_REQUIRED);
ef416fc2 180
181 /*
182 * See if we actually got the file or an error...
183 */
184
cb7f98ee 185 if (status == HTTP_STATUS_OK)
ef416fc2 186 {
187 /*
188 * Yes, copy the file...
189 */
190
a4d04587 191 while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
7e86f2f6 192 write(fd, buffer, (size_t)bytes);
ef416fc2 193 }
194 else
355e94dc
MS
195 {
196 _cupsSetHTTPError(status);
ef416fc2 197 httpFlush(http);
355e94dc 198 }
ef416fc2 199
200 /*
201 * Return the request status...
202 */
203
e07d4801
MS
204 DEBUG_printf(("1cupsGetFd: Returning %d...", status));
205
ef416fc2 206 return (status);
207}
208
209
210/*
211 * 'cupsGetFile()' - Get a file from the server.
212 *
cb7f98ee 213 * This function returns @code HTTP_STATUS_OK@ when the file is successfully retrieved.
ef416fc2 214 *
8072030b 215 * @since CUPS 1.1.20/macOS 10.4@
ef416fc2 216 */
217
ecdc0628 218http_status_t /* O - HTTP status */
568fa3fa 219cupsGetFile(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
ef416fc2 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
cb7f98ee 236 return (HTTP_STATUS_ERROR);
ef416fc2 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
cb7f98ee 251 return (HTTP_STATUS_ERROR);
ef416fc2 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
cb7f98ee 266 if (status != HTTP_STATUS_OK)
ef416fc2 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 *
cb7f98ee 280 * This function returns @code HTTP_STATUS_CREATED@ when the file is stored
5a738aea 281 * successfully.
ef416fc2 282 *
8072030b 283 * @since CUPS 1.1.20/macOS 10.4@
ef416fc2 284 */
285
ecdc0628 286http_status_t /* O - HTTP status */
568fa3fa 287cupsPutFd(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
ef416fc2 288 const char *resource, /* I - Resource name */
289 int fd) /* I - File descriptor */
290{
7e86f2f6
MS
291 ssize_t bytes; /* Number of bytes read */
292 int retries; /* Number of retries */
ef416fc2 293 char buffer[8192]; /* Buffer for file */
294 http_status_t status; /* HTTP status from server */
16f67389
MS
295 int new_auth = 0; /* Using new auth information? */
296 int digest; /* Are we using Digest authentication? */
ef416fc2 297
298
299 /*
300 * Range check input...
301 */
302
807315e6 303 DEBUG_printf(("cupsPutFd(http=%p, resource=\"%s\", fd=%d)", (void *)http, resource, fd));
ef416fc2 304
5a738aea 305 if (!resource || fd < 0)
ef416fc2 306 {
307 if (http)
308 http->error = EINVAL;
309
cb7f98ee 310 return (HTTP_STATUS_ERROR);
ef416fc2 311 }
312
5a738aea 313 if (!http)
a603edef 314 if ((http = _cupsConnect()) == NULL)
cb7f98ee 315 return (HTTP_STATUS_SERVICE_UNAVAILABLE);
5a738aea 316
ef416fc2 317 /*
318 * Then send PUT requests to the HTTP server...
319 */
320
bd7854cb 321 retries = 0;
322
ef416fc2 323 do
324 {
0c4bedc4
MS
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
e07d4801 335 DEBUG_printf(("2cupsPutFd: starting attempt, authstring=\"%s\"...",
ef416fc2 336 http->authstring));
337
338 httpClearFields(http);
ef416fc2 339 httpSetField(http, HTTP_FIELD_TRANSFER_ENCODING, "chunked");
cb7f98ee 340 httpSetExpect(http, HTTP_STATUS_CONTINUE);
ef416fc2 341
16f67389
MS
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
5737d5eb 350 _httpSetDigestAuthString(http, http->nextnonce, "PUT", resource);
16f67389
MS
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
ef416fc2 367 if (httpPut(http, resource))
368 {
cb7f98ee 369 if (httpReconnect2(http, 30000, NULL))
ef416fc2 370 {
cb7f98ee 371 status = HTTP_STATUS_ERROR;
ef416fc2 372 break;
373 }
374 else
375 {
cb7f98ee 376 status = HTTP_STATUS_UNAUTHORIZED;
ef416fc2 377 continue;
378 }
379 }
380
381 /*
b423cd4c 382 * Wait up to 1 second for a 100-continue response...
ef416fc2 383 */
384
b423cd4c 385 if (httpWait(http, 1000))
386 status = httpUpdate(http);
387 else
cb7f98ee 388 status = HTTP_STATUS_CONTINUE;
ef416fc2 389
cb7f98ee 390 if (status == HTTP_STATUS_CONTINUE)
b423cd4c 391 {
392 /*
393 * Copy the file...
394 */
ef416fc2 395
b423cd4c 396 lseek(fd, 0, SEEK_SET);
397
398 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
399 if (httpCheck(http))
400 {
cb7f98ee 401 if ((status = httpUpdate(http)) != HTTP_STATUS_CONTINUE)
b423cd4c 402 break;
403 }
404 else
7e86f2f6 405 httpWrite2(http, buffer, (size_t)bytes);
b423cd4c 406 }
ef416fc2 407
cb7f98ee 408 if (status == HTTP_STATUS_CONTINUE)
ef416fc2 409 {
a4d04587 410 httpWrite2(http, buffer, 0);
ef416fc2 411
cb7f98ee 412 while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
ef416fc2 413 }
414
cb7f98ee 415 if (status == HTTP_STATUS_ERROR && !retries)
bd7854cb 416 {
e07d4801 417 DEBUG_printf(("2cupsPutFd: retry on status %d", status));
bd7854cb 418
419 retries ++;
420
421 /* Flush any error message... */
422 httpFlush(http);
423
424 /* Reconnect... */
cb7f98ee 425 if (httpReconnect2(http, 30000, NULL))
bd7854cb 426 {
cb7f98ee 427 status = HTTP_STATUS_ERROR;
bd7854cb 428 break;
429 }
430
431 /* Try again... */
432 continue;
433 }
434
e07d4801 435 DEBUG_printf(("2cupsPutFd: status=%d", status));
ef416fc2 436
16f67389
MS
437 new_auth = 0;
438
cb7f98ee 439 if (status == HTTP_STATUS_UNAUTHORIZED)
ef416fc2 440 {
441 /*
442 * Flush any error message...
443 */
444
445 httpFlush(http);
446
447 /*
448 * See if we can do authentication...
449 */
450
16f67389
MS
451 new_auth = 1;
452
ef416fc2 453 if (cupsDoAuthentication(http, "PUT", resource))
f11a948a 454 {
cb7f98ee 455 status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
ef416fc2 456 break;
f11a948a 457 }
ef416fc2 458
cb7f98ee 459 if (httpReconnect2(http, 30000, NULL))
fa73b229 460 {
cb7f98ee 461 status = HTTP_STATUS_ERROR;
fa73b229 462 break;
463 }
ef416fc2 464
465 continue;
466 }
467#ifdef HAVE_SSL
cb7f98ee 468 else if (status == HTTP_STATUS_UPGRADE_REQUIRED)
ef416fc2 469 {
470 /* Flush any error message... */
471 httpFlush(http);
472
473 /* Reconnect... */
cb7f98ee 474 if (httpReconnect2(http, 30000, NULL))
fa73b229 475 {
cb7f98ee 476 status = HTTP_STATUS_ERROR;
fa73b229 477 break;
478 }
ef416fc2 479
480 /* Upgrade with encryption... */
cb7f98ee 481 httpEncryption(http, HTTP_ENCRYPTION_REQUIRED);
ef416fc2 482
483 /* Try again, this time with encryption enabled... */
484 continue;
485 }
486#endif /* HAVE_SSL */
487 }
cb7f98ee
MS
488 while (status == HTTP_STATUS_UNAUTHORIZED || status == HTTP_STATUS_UPGRADE_REQUIRED ||
489 (status == HTTP_STATUS_ERROR && retries < 2));
ef416fc2 490
491 /*
492 * See if we actually put the file or an error...
493 */
494
cb7f98ee 495 if (status != HTTP_STATUS_CREATED)
355e94dc
MS
496 {
497 _cupsSetHTTPError(status);
ef416fc2 498 httpFlush(http);
355e94dc 499 }
ef416fc2 500
e07d4801
MS
501 DEBUG_printf(("1cupsPutFd: Returning %d...", status));
502
ef416fc2 503 return (status);
504}
505
506
507/*
508 * 'cupsPutFile()' - Put a file on the server.
509 *
5a738aea
MS
510 * This function returns @code HTTP_CREATED@ when the file is stored
511 * successfully.
ef416fc2 512 *
8072030b 513 * @since CUPS 1.1.20/macOS 10.4@
ef416fc2 514 */
515
ecdc0628 516http_status_t /* O - HTTP status */
568fa3fa 517cupsPutFile(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
ef416fc2 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
cb7f98ee 534 return (HTTP_STATUS_ERROR);
ef416fc2 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
cb7f98ee 549 return (HTTP_STATUS_ERROR);
ef416fc2 550 }
551
552 /*
553 * Put the file...
554 */
555
556 status = cupsPutFd(http, resource, fd);
557
558 close(fd);
559
560 return (status);
561}