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