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