2 * Get/put file functions for CUPS.
4 * Copyright 2007-2018 by Apple Inc.
5 * Copyright 1997-2006 by Easy Software Products.
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
12 * Include necessary headers...
15 #include "cups-private.h"
16 #include "debug-internal.h"
19 #if defined(_WIN32) || defined(__EMX__)
23 #endif /* _WIN32 || __EMX__ */
27 * 'cupsGetFd()' - Get a file from the server.
29 * This function returns @code HTTP_STATUS_OK@ when the file is successfully retrieved.
31 * @since CUPS 1.1.20/macOS 10.4@
34 http_status_t /* O - HTTP status */
35 cupsGetFd(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
36 const char *resource, /* I - Resource name */
37 int fd) /* I - File descriptor */
39 ssize_t bytes; /* Number of bytes read */
40 char buffer[8192]; /* Buffer for file */
41 http_status_t status; /* HTTP status from server */
42 char if_modified_since[HTTP_MAX_VALUE];
43 /* If-Modified-Since header */
44 int new_auth = 0; /* Using new auth information? */
45 int digest; /* Are we using Digest authentication? */
49 * Range check input...
52 DEBUG_printf(("cupsGetFd(http=%p, resource=\"%s\", fd=%d)", (void *)http, resource, fd));
54 if (!resource || fd < 0)
59 return (HTTP_STATUS_ERROR);
63 if ((http = _cupsConnect()) == NULL)
64 return (HTTP_STATUS_SERVICE_UNAVAILABLE);
67 * Then send GET requests to the HTTP server...
70 strlcpy(if_modified_since, httpGetField(http, HTTP_FIELD_IF_MODIFIED_SINCE),
71 sizeof(if_modified_since));
75 if (!_cups_strcasecmp(httpGetField(http, HTTP_FIELD_CONNECTION), "close"))
77 httpClearFields(http);
78 if (httpReconnect2(http, 30000, NULL))
80 status = HTTP_STATUS_ERROR;
85 httpClearFields(http);
86 httpSetField(http, HTTP_FIELD_IF_MODIFIED_SINCE, if_modified_since);
88 digest = http->authstring && !strncmp(http->authstring, "Digest ", 7);
90 if (digest && !new_auth)
93 * Update the Digest authentication string...
96 _httpSetDigestAuthString(http, http->nextnonce, "GET", resource);
100 if (http->authstring && !strncmp(http->authstring, "Negotiate", 9) && !new_auth)
103 * Do not use cached Kerberos credentials since they will look like a
107 _cupsSetNegotiateAuthString(http, "GET", resource);
109 #endif /* HAVE_GSSAPI */
111 httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring);
113 if (httpGet(http, resource))
115 if (httpReconnect2(http, 30000, NULL))
117 status = HTTP_STATUS_ERROR;
122 status = HTTP_STATUS_UNAUTHORIZED;
129 while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
131 if (status == HTTP_STATUS_UNAUTHORIZED)
134 * Flush any error message...
140 * See if we can do authentication...
145 if (cupsDoAuthentication(http, "GET", resource))
147 status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
151 if (httpReconnect2(http, 30000, NULL))
153 status = HTTP_STATUS_ERROR;
160 else if (status == HTTP_STATUS_UPGRADE_REQUIRED)
162 /* Flush any error message... */
166 if (httpReconnect2(http, 30000, NULL))
168 status = HTTP_STATUS_ERROR;
172 /* Upgrade with encryption... */
173 httpEncryption(http, HTTP_ENCRYPTION_REQUIRED);
175 /* Try again, this time with encryption enabled... */
178 #endif /* HAVE_SSL */
180 while (status == HTTP_STATUS_UNAUTHORIZED || status == HTTP_STATUS_UPGRADE_REQUIRED);
183 * See if we actually got the file or an error...
186 if (status == HTTP_STATUS_OK)
189 * Yes, copy the file...
192 while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
193 write(fd, buffer, (size_t)bytes);
197 _cupsSetHTTPError(status);
202 * Return the request status...
205 DEBUG_printf(("1cupsGetFd: Returning %d...", status));
212 * 'cupsGetFile()' - Get a file from the server.
214 * This function returns @code HTTP_STATUS_OK@ when the file is successfully retrieved.
216 * @since CUPS 1.1.20/macOS 10.4@
219 http_status_t /* O - HTTP status */
220 cupsGetFile(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
221 const char *resource, /* I - Resource name */
222 const char *filename) /* I - Filename */
224 int fd; /* File descriptor */
225 http_status_t status; /* Status */
229 * Range check input...
232 if (!http || !resource || !filename)
235 http->error = EINVAL;
237 return (HTTP_STATUS_ERROR);
244 if ((fd = open(filename, O_WRONLY | O_EXCL | O_TRUNC)) < 0)
247 * Couldn't open the file!
252 return (HTTP_STATUS_ERROR);
259 status = cupsGetFd(http, resource, fd);
262 * If the file couldn't be gotten, then remove the file...
267 if (status != HTTP_STATUS_OK)
271 * Return the HTTP status code...
279 * 'cupsPutFd()' - Put a file on the server.
281 * This function returns @code HTTP_STATUS_CREATED@ when the file is stored
284 * @since CUPS 1.1.20/macOS 10.4@
287 http_status_t /* O - HTTP status */
288 cupsPutFd(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
289 const char *resource, /* I - Resource name */
290 int fd) /* I - File descriptor */
292 ssize_t bytes; /* Number of bytes read */
293 int retries; /* Number of retries */
294 char buffer[8192]; /* Buffer for file */
295 http_status_t status; /* HTTP status from server */
296 int new_auth = 0; /* Using new auth information? */
297 int digest; /* Are we using Digest authentication? */
301 * Range check input...
304 DEBUG_printf(("cupsPutFd(http=%p, resource=\"%s\", fd=%d)", (void *)http, resource, fd));
306 if (!resource || fd < 0)
309 http->error = EINVAL;
311 return (HTTP_STATUS_ERROR);
315 if ((http = _cupsConnect()) == NULL)
316 return (HTTP_STATUS_SERVICE_UNAVAILABLE);
319 * Then send PUT requests to the HTTP server...
326 if (!_cups_strcasecmp(httpGetField(http, HTTP_FIELD_CONNECTION), "close"))
328 httpClearFields(http);
329 if (httpReconnect2(http, 30000, NULL))
331 status = HTTP_STATUS_ERROR;
336 DEBUG_printf(("2cupsPutFd: starting attempt, authstring=\"%s\"...",
339 httpClearFields(http);
340 httpSetField(http, HTTP_FIELD_TRANSFER_ENCODING, "chunked");
341 httpSetExpect(http, HTTP_STATUS_CONTINUE);
343 digest = http->authstring && !strncmp(http->authstring, "Digest ", 7);
345 if (digest && !new_auth)
348 * Update the Digest authentication string...
351 _httpSetDigestAuthString(http, http->nextnonce, "PUT", resource);
355 if (http->authstring && !strncmp(http->authstring, "Negotiate", 9) && !new_auth)
358 * Do not use cached Kerberos credentials since they will look like a
362 _cupsSetNegotiateAuthString(http, "PUT", resource);
364 #endif /* HAVE_GSSAPI */
366 httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring);
368 if (httpPut(http, resource))
370 if (httpReconnect2(http, 30000, NULL))
372 status = HTTP_STATUS_ERROR;
377 status = HTTP_STATUS_UNAUTHORIZED;
383 * Wait up to 1 second for a 100-continue response...
386 if (httpWait(http, 1000))
387 status = httpUpdate(http);
389 status = HTTP_STATUS_CONTINUE;
391 if (status == HTTP_STATUS_CONTINUE)
397 lseek(fd, 0, SEEK_SET);
399 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
402 if ((status = httpUpdate(http)) != HTTP_STATUS_CONTINUE)
406 httpWrite2(http, buffer, (size_t)bytes);
409 if (status == HTTP_STATUS_CONTINUE)
411 httpWrite2(http, buffer, 0);
413 while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
416 if (status == HTTP_STATUS_ERROR && !retries)
418 DEBUG_printf(("2cupsPutFd: retry on status %d", status));
422 /* Flush any error message... */
426 if (httpReconnect2(http, 30000, NULL))
428 status = HTTP_STATUS_ERROR;
436 DEBUG_printf(("2cupsPutFd: status=%d", status));
440 if (status == HTTP_STATUS_UNAUTHORIZED)
443 * Flush any error message...
449 * See if we can do authentication...
454 if (cupsDoAuthentication(http, "PUT", resource))
456 status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
460 if (httpReconnect2(http, 30000, NULL))
462 status = HTTP_STATUS_ERROR;
469 else if (status == HTTP_STATUS_UPGRADE_REQUIRED)
471 /* Flush any error message... */
475 if (httpReconnect2(http, 30000, NULL))
477 status = HTTP_STATUS_ERROR;
481 /* Upgrade with encryption... */
482 httpEncryption(http, HTTP_ENCRYPTION_REQUIRED);
484 /* Try again, this time with encryption enabled... */
487 #endif /* HAVE_SSL */
489 while (status == HTTP_STATUS_UNAUTHORIZED || status == HTTP_STATUS_UPGRADE_REQUIRED ||
490 (status == HTTP_STATUS_ERROR && retries < 2));
493 * See if we actually put the file or an error...
496 if (status != HTTP_STATUS_CREATED)
498 _cupsSetHTTPError(status);
502 DEBUG_printf(("1cupsPutFd: Returning %d...", status));
509 * 'cupsPutFile()' - Put a file on the server.
511 * This function returns @code HTTP_CREATED@ when the file is stored
514 * @since CUPS 1.1.20/macOS 10.4@
517 http_status_t /* O - HTTP status */
518 cupsPutFile(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
519 const char *resource, /* I - Resource name */
520 const char *filename) /* I - Filename */
522 int fd; /* File descriptor */
523 http_status_t status; /* Status */
527 * Range check input...
530 if (!http || !resource || !filename)
533 http->error = EINVAL;
535 return (HTTP_STATUS_ERROR);
539 * Open the local file...
542 if ((fd = open(filename, O_RDONLY)) < 0)
545 * Couldn't open the file!
550 return (HTTP_STATUS_ERROR);
557 status = cupsPutFd(http, resource, fd);