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