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