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"
18 #if defined(_WIN32) || defined(__EMX__)
22 #endif /* _WIN32 || __EMX__ */
26 * 'cupsGetFd()' - Get a file from the server.
28 * This function returns @code HTTP_STATUS_OK@ when the file is successfully retrieved.
30 * @since CUPS 1.1.20/macOS 10.4@
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 */
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? */
48 * Range check input...
51 DEBUG_printf(("cupsGetFd(http=%p, resource=\"%s\", fd=%d)", (void *)http
, resource
, fd
));
53 if (!resource
|| fd
< 0)
58 return (HTTP_STATUS_ERROR
);
62 if ((http
= _cupsConnect()) == NULL
)
63 return (HTTP_STATUS_SERVICE_UNAVAILABLE
);
66 * Then send GET requests to the HTTP server...
69 strlcpy(if_modified_since
, httpGetField(http
, HTTP_FIELD_IF_MODIFIED_SINCE
),
70 sizeof(if_modified_since
));
74 if (!_cups_strcasecmp(httpGetField(http
, HTTP_FIELD_CONNECTION
), "close"))
76 httpClearFields(http
);
77 if (httpReconnect2(http
, 30000, NULL
))
79 status
= HTTP_STATUS_ERROR
;
84 httpClearFields(http
);
85 httpSetField(http
, HTTP_FIELD_IF_MODIFIED_SINCE
, if_modified_since
);
87 digest
= http
->authstring
&& !strncmp(http
->authstring
, "Digest ", 7);
89 if (digest
&& !new_auth
)
92 * Update the Digest authentication string...
95 _httpSetDigestAuthString(http
, http
->nextnonce
, "GET", resource
);
99 if (http
->authstring
&& !strncmp(http
->authstring
, "Negotiate", 9) && !new_auth
)
102 * Do not use cached Kerberos credentials since they will look like a
106 _cupsSetNegotiateAuthString(http
, "GET", resource
);
108 #endif /* HAVE_GSSAPI */
110 httpSetField(http
, HTTP_FIELD_AUTHORIZATION
, http
->authstring
);
112 if (httpGet(http
, resource
))
114 if (httpReconnect2(http
, 30000, NULL
))
116 status
= HTTP_STATUS_ERROR
;
121 status
= HTTP_STATUS_UNAUTHORIZED
;
128 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
130 if (status
== HTTP_STATUS_UNAUTHORIZED
)
133 * Flush any error message...
139 * See if we can do authentication...
144 if (cupsDoAuthentication(http
, "GET", resource
))
146 status
= HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED
;
150 if (httpReconnect2(http
, 30000, NULL
))
152 status
= HTTP_STATUS_ERROR
;
159 else if (status
== HTTP_STATUS_UPGRADE_REQUIRED
)
161 /* Flush any error message... */
165 if (httpReconnect2(http
, 30000, NULL
))
167 status
= HTTP_STATUS_ERROR
;
171 /* Upgrade with encryption... */
172 httpEncryption(http
, HTTP_ENCRYPTION_REQUIRED
);
174 /* Try again, this time with encryption enabled... */
177 #endif /* HAVE_SSL */
179 while (status
== HTTP_STATUS_UNAUTHORIZED
|| status
== HTTP_STATUS_UPGRADE_REQUIRED
);
182 * See if we actually got the file or an error...
185 if (status
== HTTP_STATUS_OK
)
188 * Yes, copy the file...
191 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
192 write(fd
, buffer
, (size_t)bytes
);
196 _cupsSetHTTPError(status
);
201 * Return the request status...
204 DEBUG_printf(("1cupsGetFd: Returning %d...", status
));
211 * 'cupsGetFile()' - Get a file from the server.
213 * This function returns @code HTTP_STATUS_OK@ when the file is successfully retrieved.
215 * @since CUPS 1.1.20/macOS 10.4@
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 */
223 int fd
; /* File descriptor */
224 http_status_t status
; /* Status */
228 * Range check input...
231 if (!http
|| !resource
|| !filename
)
234 http
->error
= EINVAL
;
236 return (HTTP_STATUS_ERROR
);
243 if ((fd
= open(filename
, O_WRONLY
| O_EXCL
| O_TRUNC
)) < 0)
246 * Couldn't open the file!
251 return (HTTP_STATUS_ERROR
);
258 status
= cupsGetFd(http
, resource
, fd
);
261 * If the file couldn't be gotten, then remove the file...
266 if (status
!= HTTP_STATUS_OK
)
270 * Return the HTTP status code...
278 * 'cupsPutFd()' - Put a file on the server.
280 * This function returns @code HTTP_STATUS_CREATED@ when the file is stored
283 * @since CUPS 1.1.20/macOS 10.4@
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 */
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? */
300 * Range check input...
303 DEBUG_printf(("cupsPutFd(http=%p, resource=\"%s\", fd=%d)", (void *)http
, resource
, fd
));
305 if (!resource
|| fd
< 0)
308 http
->error
= EINVAL
;
310 return (HTTP_STATUS_ERROR
);
314 if ((http
= _cupsConnect()) == NULL
)
315 return (HTTP_STATUS_SERVICE_UNAVAILABLE
);
318 * Then send PUT requests to the HTTP server...
325 if (!_cups_strcasecmp(httpGetField(http
, HTTP_FIELD_CONNECTION
), "close"))
327 httpClearFields(http
);
328 if (httpReconnect2(http
, 30000, NULL
))
330 status
= HTTP_STATUS_ERROR
;
335 DEBUG_printf(("2cupsPutFd: starting attempt, authstring=\"%s\"...",
338 httpClearFields(http
);
339 httpSetField(http
, HTTP_FIELD_TRANSFER_ENCODING
, "chunked");
340 httpSetExpect(http
, HTTP_STATUS_CONTINUE
);
342 digest
= http
->authstring
&& !strncmp(http
->authstring
, "Digest ", 7);
344 if (digest
&& !new_auth
)
347 * Update the Digest authentication string...
350 _httpSetDigestAuthString(http
, http
->nextnonce
, "PUT", resource
);
354 if (http
->authstring
&& !strncmp(http
->authstring
, "Negotiate", 9) && !new_auth
)
357 * Do not use cached Kerberos credentials since they will look like a
361 _cupsSetNegotiateAuthString(http
, "PUT", resource
);
363 #endif /* HAVE_GSSAPI */
365 httpSetField(http
, HTTP_FIELD_AUTHORIZATION
, http
->authstring
);
367 if (httpPut(http
, resource
))
369 if (httpReconnect2(http
, 30000, NULL
))
371 status
= HTTP_STATUS_ERROR
;
376 status
= HTTP_STATUS_UNAUTHORIZED
;
382 * Wait up to 1 second for a 100-continue response...
385 if (httpWait(http
, 1000))
386 status
= httpUpdate(http
);
388 status
= HTTP_STATUS_CONTINUE
;
390 if (status
== HTTP_STATUS_CONTINUE
)
396 lseek(fd
, 0, SEEK_SET
);
398 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
401 if ((status
= httpUpdate(http
)) != HTTP_STATUS_CONTINUE
)
405 httpWrite2(http
, buffer
, (size_t)bytes
);
408 if (status
== HTTP_STATUS_CONTINUE
)
410 httpWrite2(http
, buffer
, 0);
412 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
415 if (status
== HTTP_STATUS_ERROR
&& !retries
)
417 DEBUG_printf(("2cupsPutFd: retry on status %d", status
));
421 /* Flush any error message... */
425 if (httpReconnect2(http
, 30000, NULL
))
427 status
= HTTP_STATUS_ERROR
;
435 DEBUG_printf(("2cupsPutFd: status=%d", status
));
439 if (status
== HTTP_STATUS_UNAUTHORIZED
)
442 * Flush any error message...
448 * See if we can do authentication...
453 if (cupsDoAuthentication(http
, "PUT", resource
))
455 status
= HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED
;
459 if (httpReconnect2(http
, 30000, NULL
))
461 status
= HTTP_STATUS_ERROR
;
468 else if (status
== HTTP_STATUS_UPGRADE_REQUIRED
)
470 /* Flush any error message... */
474 if (httpReconnect2(http
, 30000, NULL
))
476 status
= HTTP_STATUS_ERROR
;
480 /* Upgrade with encryption... */
481 httpEncryption(http
, HTTP_ENCRYPTION_REQUIRED
);
483 /* Try again, this time with encryption enabled... */
486 #endif /* HAVE_SSL */
488 while (status
== HTTP_STATUS_UNAUTHORIZED
|| status
== HTTP_STATUS_UPGRADE_REQUIRED
||
489 (status
== HTTP_STATUS_ERROR
&& retries
< 2));
492 * See if we actually put the file or an error...
495 if (status
!= HTTP_STATUS_CREATED
)
497 _cupsSetHTTPError(status
);
501 DEBUG_printf(("1cupsPutFd: Returning %d...", status
));
508 * 'cupsPutFile()' - Put a file on the server.
510 * This function returns @code HTTP_CREATED@ when the file is stored
513 * @since CUPS 1.1.20/macOS 10.4@
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 */
521 int fd
; /* File descriptor */
522 http_status_t status
; /* Status */
526 * Range check input...
529 if (!http
|| !resource
|| !filename
)
532 http
->error
= EINVAL
;
534 return (HTTP_STATUS_ERROR
);
538 * Open the local file...
541 if ((fd
= open(filename
, O_RDONLY
)) < 0)
544 * Couldn't open the file!
549 return (HTTP_STATUS_ERROR
);
556 status
= cupsPutFd(http
, resource
, fd
);