1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2018, 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 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 "strcase.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 #include "tool_cb_wrt.h"
35
36 #include "memdebug.h" /* keep this as LAST include */
37
38 static char *parse_filename(const char *ptr, size_t len);
39
40 #ifdef WIN32
41 #define BOLD
42 #define BOLDOFF
43 #else
44 #define BOLD "\x1b[1m"
45 /* Switch off bold by setting "all attributes off" since the explicit
46 bold-off code (21) isn't supported everywhere - like in the mac
47 Terminal. */
48 #define BOLDOFF "\x1b[0m"
49 #endif
50
51 /*
52 ** callback for CURLOPT_HEADERFUNCTION
53 */
54
tool_header_cb(char * ptr,size_t size,size_t nmemb,void * userdata)55 size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
56 {
57 struct HdrCbData *hdrcbdata = userdata;
58 struct OutStruct *outs = hdrcbdata->outs;
59 struct OutStruct *heads = hdrcbdata->heads;
60 const char *str = ptr;
61 const size_t cb = size * nmemb;
62 const char *end = (char *)ptr + cb;
63 long protocol = 0;
64
65 /*
66 * Once that libcurl has called back tool_header_cb() the returned value
67 * is checked against the amount that was intended to be written, if
68 * it does not match then it fails with CURLE_WRITE_ERROR. So at this
69 * point returning a value different from sz*nmemb indicates failure.
70 */
71 size_t failure = (size && nmemb) ? 0 : 1;
72
73 if(!heads->config)
74 return failure;
75
76 #ifdef DEBUGBUILD
77 if(size * nmemb > (size_t)CURL_MAX_HTTP_HEADER) {
78 warnf(heads->config->global, "Header data exceeds single call write "
79 "limit!\n");
80 return failure;
81 }
82 #endif
83
84 /*
85 * Write header data when curl option --dump-header (-D) is given.
86 */
87
88 if(heads->config->headerfile && heads->stream) {
89 size_t rc = fwrite(ptr, size, nmemb, heads->stream);
90 if(rc != cb)
91 return rc;
92 /* flush the stream to send off what we got earlier */
93 (void)fflush(heads->stream);
94 }
95
96 /*
97 * This callback sets the filename where output shall be written when
98 * curl options --remote-name (-O) and --remote-header-name (-J) have
99 * been simultaneously given and additionally server returns an HTTP
100 * Content-Disposition header specifying a filename property.
101 */
102
103 curl_easy_getinfo(outs->config->easy, CURLINFO_PROTOCOL, &protocol);
104 if(hdrcbdata->honor_cd_filename &&
105 (cb > 20) && checkprefix("Content-disposition:", str) &&
106 (protocol & (CURLPROTO_HTTPS|CURLPROTO_HTTP))) {
107 const char *p = str + 20;
108
109 /* look for the 'filename=' parameter
110 (encoded filenames (*=) are not supported) */
111 for(;;) {
112 char *filename;
113 size_t len;
114
115 while(*p && (p < end) && !ISALPHA(*p))
116 p++;
117 if(p > end - 9)
118 break;
119
120 if(memcmp(p, "filename=", 9)) {
121 /* no match, find next parameter */
122 while((p < end) && (*p != ';'))
123 p++;
124 continue;
125 }
126 p += 9;
127
128 /* this expression below typecasts 'cb' only to avoid
129 warning: signed and unsigned type in conditional expression
130 */
131 len = (ssize_t)cb - (p - str);
132 filename = parse_filename(p, len);
133 if(filename) {
134 if(outs->stream) {
135 int rc;
136 /* already opened and possibly written to */
137 if(outs->fopened)
138 fclose(outs->stream);
139 outs->stream = NULL;
140
141 /* rename the initial file name to the new file name */
142 rc = rename(outs->filename, filename);
143 if(rc != 0) {
144 warnf(outs->config->global, "Failed to rename %s -> %s: %s\n",
145 outs->filename, filename, strerror(errno));
146 }
147 if(outs->alloc_filename)
148 Curl_safefree(outs->filename);
149 if(rc != 0) {
150 free(filename);
151 return failure;
152 }
153 }
154 outs->is_cd_filename = TRUE;
155 outs->s_isreg = TRUE;
156 outs->fopened = FALSE;
157 outs->filename = filename;
158 outs->alloc_filename = TRUE;
159 hdrcbdata->honor_cd_filename = FALSE; /* done now! */
160 if(!tool_create_output_file(outs))
161 return failure;
162 }
163 break;
164 }
165 if(!outs->stream && !tool_create_output_file(outs))
166 return failure;
167 }
168
169 if(hdrcbdata->config->show_headers &&
170 (protocol &
171 (CURLPROTO_HTTP|CURLPROTO_HTTPS|CURLPROTO_RTSP|CURLPROTO_FILE))) {
172 /* bold headers only for selected protocols */
173 char *value = NULL;
174
175 if(!outs->stream && !tool_create_output_file(outs))
176 return failure;
177
178 if(hdrcbdata->global->isatty && hdrcbdata->global->styled_output)
179 value = memchr(ptr, ':', cb);
180 if(value) {
181 size_t namelen = value - ptr;
182 fprintf(outs->stream, BOLD "%.*s" BOLDOFF ":", namelen, ptr);
183 fwrite(&value[1], cb - namelen - 1, 1, outs->stream);
184 }
185 else
186 /* not "handled", just show it */
187 fwrite(ptr, cb, 1, outs->stream);
188 }
189 return cb;
190 }
191
192 /*
193 * Copies a file name part and returns an ALLOCATED data buffer.
194 */
parse_filename(const char * ptr,size_t len)195 static char *parse_filename(const char *ptr, size_t len)
196 {
197 char *copy;
198 char *p;
199 char *q;
200 char stop = '\0';
201
202 /* simple implementation of strndup() */
203 copy = malloc(len + 1);
204 if(!copy)
205 return NULL;
206 memcpy(copy, ptr, len);
207 copy[len] = '\0';
208
209 p = copy;
210 if(*p == '\'' || *p == '"') {
211 /* store the starting quote */
212 stop = *p;
213 p++;
214 }
215 else
216 stop = ';';
217
218 /* scan for the end letter and stop there */
219 q = strchr(p, stop);
220 if(q)
221 *q = '\0';
222
223 /* if the filename contains a path, only use filename portion */
224 q = strrchr(p, '/');
225 if(q) {
226 p = q + 1;
227 if(!*p) {
228 Curl_safefree(copy);
229 return NULL;
230 }
231 }
232
233 /* If the filename contains a backslash, only use filename portion. The idea
234 is that even systems that don't handle backslashes as path separators
235 probably want the path removed for convenience. */
236 q = strrchr(p, '\\');
237 if(q) {
238 p = q + 1;
239 if(!*p) {
240 Curl_safefree(copy);
241 return NULL;
242 }
243 }
244
245 /* make sure the file name doesn't end in \r or \n */
246 q = strchr(p, '\r');
247 if(q)
248 *q = '\0';
249
250 q = strchr(p, '\n');
251 if(q)
252 *q = '\0';
253
254 if(copy != p)
255 memmove(copy, p, strlen(p) + 1);
256
257 #if defined(MSDOS) || defined(WIN32)
258 {
259 char *sanitized;
260 SANITIZEcode sc = sanitize_file_name(&sanitized, copy, 0);
261 Curl_safefree(copy);
262 if(sc)
263 return NULL;
264 copy = sanitized;
265 }
266 #endif /* MSDOS || WIN32 */
267
268 /* in case we built debug enabled, we allow an environment variable
269 * named CURL_TESTDIR to prefix the given file name to put it into a
270 * specific directory
271 */
272 #ifdef DEBUGBUILD
273 {
274 char *tdir = curlx_getenv("CURL_TESTDIR");
275 if(tdir) {
276 char buffer[512]; /* suitably large */
277 msnprintf(buffer, sizeof(buffer), "%s/%s", tdir, copy);
278 Curl_safefree(copy);
279 copy = strdup(buffer); /* clone the buffer, we don't use the libcurl
280 aprintf() or similar since we want to use the
281 same memory code as the "real" parse_filename
282 function */
283 curl_free(tdir);
284 }
285 }
286 #endif
287
288 return copy;
289 }
290