Home | History | Annotate | Download | only in src
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel (at) haxx.se>, et al.
      9  *
     10  * This software is licensed as described in the file COPYING, which
     11  * you should have received as part of this distribution. The terms
     12  * are also available at https://curl.haxx.se/docs/copyright.html.
     13  *
     14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     15  * copies of the Software, and permit persons to whom the Software is
     16  * furnished to do so, under the terms of the COPYING file.
     17  *
     18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     19  * KIND, either express or implied.
     20  *
     21  ***************************************************************************/
     22 #include "tool_setup.h"
     23 
     24 #define ENABLE_CURLX_PRINTF
     25 /* use our own printf() functions */
     26 #include "curlx.h"
     27 
     28 #include "tool_cfgable.h"
     29 #include "tool_msgs.h"
     30 #include "tool_cb_wrt.h"
     31 
     32 #include "memdebug.h" /* keep this as LAST include */
     33 
     34 /* create a local file for writing, return TRUE on success */
     35 bool tool_create_output_file(struct OutStruct *outs)
     36 {
     37   struct GlobalConfig *global = outs->config->global;
     38   FILE *file;
     39 
     40   if(!outs->filename || !*outs->filename) {
     41     warnf(global, "Remote filename has no length!\n");
     42     return FALSE;
     43   }
     44 
     45   if(outs->is_cd_filename) {
     46     /* don't overwrite existing files */
     47     file = fopen(outs->filename, "rb");
     48     if(file) {
     49       fclose(file);
     50       warnf(global, "Refusing to overwrite %s: %s\n", outs->filename,
     51             strerror(EEXIST));
     52       return FALSE;
     53     }
     54   }
     55 
     56   /* open file for writing */
     57   file = fopen(outs->filename, "wb");
     58   if(!file) {
     59     warnf(global, "Failed to create the file %s: %s\n", outs->filename,
     60           strerror(errno));
     61     return FALSE;
     62   }
     63   outs->s_isreg = TRUE;
     64   outs->fopened = TRUE;
     65   outs->stream = file;
     66   outs->bytes = 0;
     67   outs->init = 0;
     68   return TRUE;
     69 }
     70 
     71 /*
     72 ** callback for CURLOPT_WRITEFUNCTION
     73 */
     74 
     75 size_t tool_write_cb(void *buffer, size_t sz, size_t nmemb, void *userdata)
     76 {
     77   size_t rc;
     78   struct OutStruct *outs = userdata;
     79   struct OperationConfig *config = outs->config;
     80 
     81   /*
     82    * Once that libcurl has called back tool_write_cb() the returned value
     83    * is checked against the amount that was intended to be written, if
     84    * it does not match then it fails with CURLE_WRITE_ERROR. So at this
     85    * point returning a value different from sz*nmemb indicates failure.
     86    */
     87   const size_t failure = (sz * nmemb) ? 0 : 1;
     88 
     89   if(!config)
     90     return failure;
     91 
     92 #ifdef DEBUGBUILD
     93   if(config->include_headers) {
     94     if(sz * nmemb > (size_t)CURL_MAX_HTTP_HEADER) {
     95       warnf(config->global, "Header data size exceeds single call write "
     96             "limit!\n");
     97       return failure;
     98     }
     99   }
    100   else {
    101     if(sz * nmemb > (size_t)CURL_MAX_WRITE_SIZE) {
    102       warnf(config->global, "Data size exceeds single call write limit!\n");
    103       return failure;
    104     }
    105   }
    106 
    107   {
    108     /* Some internal congruency checks on received OutStruct */
    109     bool check_fails = FALSE;
    110     if(outs->filename) {
    111       /* regular file */
    112       if(!*outs->filename)
    113         check_fails = TRUE;
    114       if(!outs->s_isreg)
    115         check_fails = TRUE;
    116       if(outs->fopened && !outs->stream)
    117         check_fails = TRUE;
    118       if(!outs->fopened && outs->stream)
    119         check_fails = TRUE;
    120       if(!outs->fopened && outs->bytes)
    121         check_fails = TRUE;
    122     }
    123     else {
    124       /* standard stream */
    125       if(!outs->stream || outs->s_isreg || outs->fopened)
    126         check_fails = TRUE;
    127       if(outs->alloc_filename || outs->is_cd_filename || outs->init)
    128         check_fails = TRUE;
    129     }
    130     if(check_fails) {
    131       warnf(config->global, "Invalid output struct data for write callback\n");
    132       return failure;
    133     }
    134   }
    135 #endif
    136 
    137   if(!outs->stream && !tool_create_output_file(outs))
    138     return failure;
    139 
    140   rc = fwrite(buffer, sz, nmemb, outs->stream);
    141 
    142   if((sz * nmemb) == rc)
    143     /* we added this amount of data to the output */
    144     outs->bytes += (sz * nmemb);
    145 
    146   if(config->readbusy) {
    147     config->readbusy = FALSE;
    148     curl_easy_pause(config->easy, CURLPAUSE_CONT);
    149   }
    150 
    151   if(config->nobuffer) {
    152     /* output buffering disabled */
    153     int res = fflush(outs->stream);
    154     if(res)
    155       return failure;
    156   }
    157 
    158   return rc;
    159 }
    160