1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2011, 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 "setup.h" 23 24#include <curl/curl.h> 25 26#include "rawstr.h" 27 28#define ENABLE_CURLX_PRINTF 29/* use our own printf() functions */ 30#include "curlx.h" 31 32#include "tool_cfgable.h" 33#include "tool_msgs.h" 34#include "tool_cb_hdr.h" 35 36#include "memdebug.h" /* keep this as LAST include */ 37 38static char *parse_filename(const char *ptr, size_t len); 39 40/* 41** callback for CURLOPT_HEADERFUNCTION 42*/ 43 44size_t tool_header_cb(void *ptr, size_t size, size_t nmemb, void *userdata) 45{ 46 struct OutStruct *outs = userdata; 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(!outs->config) 60 return failure; 61 62#ifdef DEBUGBUILD 63 if(size * nmemb > (size_t)CURL_MAX_HTTP_HEADER) { 64 warnf(outs->config, "Header data exceeds single call write limit!\n"); 65 return failure; 66 } 67#endif 68 69 if(!outs->filename && (cb > 20) && 70 checkprefix("Content-disposition:", str)) { 71 const char *p = str + 20; 72 73 /* look for the 'filename=' parameter 74 (encoded filenames (*=) are not supported) */ 75 for(;;) { 76 char *filename; 77 size_t len; 78 79 while(*p && (p < end) && !ISALPHA(*p)) 80 p++; 81 if(p > end - 9) 82 break; 83 84 if(memcmp(p, "filename=", 9)) { 85 /* no match, find next parameter */ 86 while((p < end) && (*p != ';')) 87 p++; 88 continue; 89 } 90 p += 9; 91 92 /* this expression below typecasts 'cb' only to avoid 93 warning: signed and unsigned type in conditional expression 94 */ 95 len = (ssize_t)cb - (p - str); 96 filename = parse_filename(p, len); 97 if(filename) { 98 outs->filename = filename; 99 outs->alloc_filename = TRUE; 100 outs->s_isreg = TRUE; 101 outs->fopened = FALSE; 102 outs->stream = NULL; 103 break; 104 } 105 else 106 return failure; 107 } 108 } 109 110 return cb; 111} 112 113/* 114 * Copies a file name part and returns an ALLOCATED data buffer. 115 */ 116static char *parse_filename(const char *ptr, size_t len) 117{ 118 char *copy; 119 char *p; 120 char *q; 121 char stop = '\0'; 122 123 /* simple implementation of strndup() */ 124 copy = malloc(len+1); 125 if(!copy) 126 return NULL; 127 memcpy(copy, ptr, len); 128 copy[len] = '\0'; 129 130 p = copy; 131 if(*p == '\'' || *p == '"') { 132 /* store the starting quote */ 133 stop = *p; 134 p++; 135 } 136 else 137 stop = ';'; 138 139 /* if the filename contains a path, only use filename portion */ 140 q = strrchr(copy, '/'); 141 if(q) { 142 p = q + 1; 143 if(!*p) { 144 Curl_safefree(copy); 145 return NULL; 146 } 147 } 148 149 /* If the filename contains a backslash, only use filename portion. The idea 150 is that even systems that don't handle backslashes as path separators 151 probably want the path removed for convenience. */ 152 q = strrchr(p, '\\'); 153 if(q) { 154 p = q + 1; 155 if(!*p) { 156 Curl_safefree(copy); 157 return NULL; 158 } 159 } 160 161 /* scan for the end letter and stop there */ 162 q = p; 163 while(*q) { 164 if(q[1] && (q[0] == '\\')) 165 q++; 166 else if(q[0] == stop) 167 break; 168 q++; 169 } 170 *q = '\0'; 171 172 /* make sure the file name doesn't end in \r or \n */ 173 q = strchr(p, '\r'); 174 if(q) 175 *q = '\0'; 176 177 q = strchr(p, '\n'); 178 if(q) 179 *q = '\0'; 180 181 if(copy != p) 182 memmove(copy, p, strlen(p) + 1); 183 184 /* in case we built debug enabled, we allow an evironment variable 185 * named CURL_TESTDIR to prefix the given file name to put it into a 186 * specific directory 187 */ 188#ifdef DEBUGBUILD 189 { 190 char *tdir = curlx_getenv("CURL_TESTDIR"); 191 if(tdir) { 192 char buffer[512]; /* suitably large */ 193 snprintf(buffer, sizeof(buffer), "%s/%s", tdir, copy); 194 Curl_safefree(copy); 195 copy = strdup(buffer); /* clone the buffer, we don't use the libcurl 196 aprintf() or similar since we want to use the 197 same memory code as the "real" parse_filename 198 function */ 199 curl_free(tdir); 200 } 201 } 202#endif 203 204 return copy; 205} 206 207