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