Home | History | Annotate | Download | only in cups
      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 }
    507