• 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  * 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 */
cupsGetFd(http_t * http,const char * resource,int fd)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 */
cupsGetFile(http_t * http,const char * resource,const char * filename)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 */
cupsPutFd(http_t * http,const char * resource,int fd)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 */
cupsPutFile(http_t * http,const char * resource,const char * filename)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 }
563