• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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