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