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
);