Skip to content

Commit

Permalink
tool_cb_hdr: allow --remote-header-name for all http responses
Browse files Browse the repository at this point in the history
- Use the content-disposition filename (server-specified filename) for
  non-2xx responses.

For example, a server may send a content-disposition filename header
with a redirect reply (3xx) but not with the final response (2xx).
Without this change curl would ignore the server's specified filename
and continue to use the filename extracted from the user-specified URL.

This is a partial revert of 75d79a4, which limited content-disposition
and etag header processing to 2xx response codes. There's no explanation
for why that was done.

Reported-by: Morgan Willcock

Fixes curl#13302
Closes #xxxx
  • Loading branch information
jay committed Apr 6, 2024
1 parent bf567dd commit fd56bef
Showing 1 changed file with 69 additions and 67 deletions.
136 changes: 69 additions & 67 deletions src/tool_cb_hdr.c
Expand Up @@ -107,19 +107,20 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)

curl_easy_getinfo(per->curl, CURLINFO_SCHEME, &scheme);
scheme = proto_token(scheme);

/*
* Write etag to file when --etag-save option is given.
*/

if((scheme == proto_http || scheme == proto_https)) {
long response = 0;
curl_easy_getinfo(per->curl, CURLINFO_RESPONSE_CODE, &response);

if(response/100 != 2)
/* only care about these headers in 2xx responses */
;
/*
* Write etag to file when --etag-save option is given.
*/
else if(per->config->etag_save_file && etag_save->stream &&
/* match only header that start with etag (case insensitive) */
checkprefix("etag:", str)) {
if(per->config->etag_save_file && etag_save->stream &&
/* match only header that start with etag (case insensitive) */
checkprefix("etag:", str) &&
/* only care about etag headers in 2xx responses */
(response/100 == 2)) {
const char *etag_h = &str[5];
const char *eot = end - 1;
if(*eot == '\n') {
Expand All @@ -137,76 +138,77 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
}
}
}
}

/*
* This callback sets the filename where output shall be written when
* curl options --remote-name (-O) and --remote-header-name (-J) have
* been simultaneously given and additionally server returns an HTTP
* Content-Disposition header specifying a filename property.
*/
/*
* This callback sets the filename where output shall be written when
* curl options --remote-name (-O) and --remote-header-name (-J) have
* been simultaneously given and additionally server returns an HTTP
* Content-Disposition header specifying a filename property.
*/

else if(hdrcbdata->honor_cd_filename &&
(cb > 20) && checkprefix("Content-disposition:", str)) {
const char *p = str + 20;
if(hdrcbdata->honor_cd_filename &&
(cb > 20) && checkprefix("Content-disposition:", str) &&
(scheme == proto_http || scheme == proto_https)) {
const char *p = str + 20;

/* look for the 'filename=' parameter
(encoded filenames (*=) are not supported) */
for(;;) {
char *filename;
size_t len;
/* look for the 'filename=' parameter
(encoded filenames (*=) are not supported) */
for(;;) {
char *filename;
size_t len;

while((p < end) && *p && !ISALPHA(*p))
p++;
if(p > end - 9)
break;

while((p < end) && *p && !ISALPHA(*p))
if(memcmp(p, "filename=", 9)) {
/* no match, find next parameter */
while((p < end) && *p && (*p != ';'))
p++;
if(p > end - 9)
if((p < end) && *p)
continue;
else
break;

if(memcmp(p, "filename=", 9)) {
/* no match, find next parameter */
while((p < end) && *p && (*p != ';'))
p++;
if((p < end) && *p)
continue;
else
break;
}
p += 9;

/* this expression below typecasts 'cb' only to avoid
warning: signed and unsigned type in conditional expression
*/
len = (ssize_t)cb - (p - str);
filename = parse_filename(p, len);
if(filename) {
if(outs->stream) {
/* indication of problem, get out! */
free(filename);
return CURL_WRITEFUNC_ERROR;
}
p += 9;

/* this expression below typecasts 'cb' only to avoid
warning: signed and unsigned type in conditional expression
*/
len = (ssize_t)cb - (p - str);
filename = parse_filename(p, len);
if(filename) {
if(outs->stream) {
/* indication of problem, get out! */
free(filename);
return CURL_WRITEFUNC_ERROR;
}

if(per->config->output_dir) {
outs->filename = aprintf("%s/%s", per->config->output_dir,
filename);
free(filename);
if(!outs->filename)
return CURL_WRITEFUNC_ERROR;
}
else
outs->filename = filename;

outs->is_cd_filename = TRUE;
outs->s_isreg = TRUE;
outs->fopened = FALSE;
outs->alloc_filename = TRUE;
hdrcbdata->honor_cd_filename = FALSE; /* done now! */
if(!tool_create_output_file(outs, per->config))

if(per->config->output_dir) {
outs->filename = aprintf("%s/%s", per->config->output_dir, filename);
free(filename);
if(!outs->filename)
return CURL_WRITEFUNC_ERROR;
}
break;
else
outs->filename = filename;

outs->is_cd_filename = TRUE;
outs->s_isreg = TRUE;
outs->fopened = FALSE;
outs->alloc_filename = TRUE;
hdrcbdata->honor_cd_filename = FALSE; /* done now! */
if(!tool_create_output_file(outs, per->config))
return CURL_WRITEFUNC_ERROR;
}
if(!outs->stream && !tool_create_output_file(outs, per->config))
return CURL_WRITEFUNC_ERROR;
break;
}
if(!outs->stream && !tool_create_output_file(outs, per->config))
return CURL_WRITEFUNC_ERROR;
}

if(hdrcbdata->config->writeout) {
char *value = memchr(ptr, ':', cb);
if(value) {
Expand Down

0 comments on commit fd56bef

Please sign in to comment.