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 #include "rawstr.h" 25 26 #define ENABLE_CURLX_PRINTF 27 /* use our own printf() functions */ 28 #include "curlx.h" 29 30 #include "tool_cfgable.h" 31 #include "tool_doswin.h" 32 #include "tool_msgs.h" 33 #include "tool_cb_hdr.h" 34 35 #include "memdebug.h" /* keep this as LAST include */ 36 37 static char *parse_filename(const char *ptr, size_t len); 38 39 /* 40 ** callback for CURLOPT_HEADERFUNCTION 41 */ 42 43 size_t tool_header_cb(void *ptr, size_t size, size_t nmemb, void *userdata) 44 { 45 struct HdrCbData *hdrcbdata = userdata; 46 struct OutStruct *outs = hdrcbdata->outs; 47 struct OutStruct *heads = hdrcbdata->heads; 48 const char *str = ptr; 49 const size_t cb = size * nmemb; 50 const char *end = (char*)ptr + cb; 51 char *url = NULL; 52 53 /* 54 * Once that libcurl has called back tool_header_cb() the returned value 55 * is checked against the amount that was intended to be written, if 56 * it does not match then it fails with CURLE_WRITE_ERROR. So at this 57 * point returning a value different from sz*nmemb indicates failure. 58 */ 59 size_t failure = (size * nmemb) ? 0 : 1; 60 61 if(!heads->config) 62 return failure; 63 64 #ifdef DEBUGBUILD 65 if(size * nmemb > (size_t)CURL_MAX_HTTP_HEADER) { 66 warnf(heads->config->global, "Header data exceeds single call write " 67 "limit!\n"); 68 return failure; 69 } 70 #endif 71 72 /* 73 * Write header data when curl option --dump-header (-D) is given. 74 */ 75 76 if(heads->config->headerfile && heads->stream) { 77 size_t rc = fwrite(ptr, size, nmemb, heads->stream); 78 if(rc != cb) 79 return rc; 80 /* flush the stream to send off what we got earlier */ 81 (void)fflush(heads->stream); 82 } 83 84 /* 85 * This callback sets the filename where output shall be written when 86 * curl options --remote-name (-O) and --remote-header-name (-J) have 87 * been simultaneously given and additionally server returns an HTTP 88 * Content-Disposition header specifying a filename property. 89 */ 90 91 if(hdrcbdata->honor_cd_filename && 92 (cb > 20) && checkprefix("Content-disposition:", str) && 93 !curl_easy_getinfo(outs->config->easy, CURLINFO_EFFECTIVE_URL, &url) && 94 url && (checkprefix("http://", url) || checkprefix("https://", url))) { 95 const char *p = str + 20; 96 97 /* look for the 'filename=' parameter 98 (encoded filenames (*=) are not supported) */ 99 for(;;) { 100 char *filename; 101 size_t len; 102 103 while(*p && (p < end) && !ISALPHA(*p)) 104 p++; 105 if(p > end - 9) 106 break; 107 108 if(memcmp(p, "filename=", 9)) { 109 /* no match, find next parameter */ 110 while((p < end) && (*p != ';')) 111 p++; 112 continue; 113 } 114 p += 9; 115 116 /* this expression below typecasts 'cb' only to avoid 117 warning: signed and unsigned type in conditional expression 118 */ 119 len = (ssize_t)cb - (p - str); 120 filename = parse_filename(p, len); 121 if(filename) { 122 outs->filename = filename; 123 outs->alloc_filename = TRUE; 124 outs->is_cd_filename = TRUE; 125 outs->s_isreg = TRUE; 126 outs->fopened = FALSE; 127 outs->stream = NULL; 128 hdrcbdata->honor_cd_filename = FALSE; 129 break; 130 } 131 else 132 return failure; 133 } 134 } 135 136 return cb; 137 } 138 139 /* 140 * Copies a file name part and returns an ALLOCATED data buffer. 141 */ 142 static char *parse_filename(const char *ptr, size_t len) 143 { 144 char *copy; 145 char *p; 146 char *q; 147 char stop = '\0'; 148 149 /* simple implementation of strndup() */ 150 copy = malloc(len+1); 151 if(!copy) 152 return NULL; 153 memcpy(copy, ptr, len); 154 copy[len] = '\0'; 155 156 p = copy; 157 if(*p == '\'' || *p == '"') { 158 /* store the starting quote */ 159 stop = *p; 160 p++; 161 } 162 else 163 stop = ';'; 164 165 /* if the filename contains a path, only use filename portion */ 166 q = strrchr(copy, '/'); 167 if(q) { 168 p = q + 1; 169 if(!*p) { 170 Curl_safefree(copy); 171 return NULL; 172 } 173 } 174 175 /* If the filename contains a backslash, only use filename portion. The idea 176 is that even systems that don't handle backslashes as path separators 177 probably want the path removed for convenience. */ 178 q = strrchr(p, '\\'); 179 if(q) { 180 p = q + 1; 181 if(!*p) { 182 Curl_safefree(copy); 183 return NULL; 184 } 185 } 186 187 /* scan for the end letter and stop there */ 188 for(q = p; *q; ++q) { 189 if(*q == stop) { 190 *q = '\0'; 191 break; 192 } 193 } 194 195 /* make sure the file name doesn't end in \r or \n */ 196 q = strchr(p, '\r'); 197 if(q) 198 *q = '\0'; 199 200 q = strchr(p, '\n'); 201 if(q) 202 *q = '\0'; 203 204 if(copy != p) 205 memmove(copy, p, strlen(p) + 1); 206 207 #if defined(MSDOS) || defined(WIN32) 208 { 209 char *sanitized; 210 SANITIZEcode sc = sanitize_file_name(&sanitized, copy, 0); 211 Curl_safefree(copy); 212 if(sc) 213 return NULL; 214 copy = sanitized; 215 } 216 #endif /* MSDOS || WIN32 */ 217 218 /* in case we built debug enabled, we allow an evironment variable 219 * named CURL_TESTDIR to prefix the given file name to put it into a 220 * specific directory 221 */ 222 #ifdef DEBUGBUILD 223 { 224 char *tdir = curlx_getenv("CURL_TESTDIR"); 225 if(tdir) { 226 char buffer[512]; /* suitably large */ 227 snprintf(buffer, sizeof(buffer), "%s/%s", tdir, copy); 228 Curl_safefree(copy); 229 copy = strdup(buffer); /* clone the buffer, we don't use the libcurl 230 aprintf() or similar since we want to use the 231 same memory code as the "real" parse_filename 232 function */ 233 curl_free(tdir); 234 } 235 } 236 #endif 237 238 return copy; 239 } 240 241