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