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