tool: support --show-headers AND --remote-header-name

By keeping the headers in memory until we know the target file name,
then output them all.

Previously this option combination would cause an error.

Add test 1310 and 1492 to verify. Adjusted test 1460 to work in the new
conditions.

Closes #15110
This commit is contained in:
Daniel Stenberg 2024-09-30 15:38:56 +02:00
parent bc6072d245
commit b0c82239c2
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
12 changed files with 240 additions and 69 deletions

View file

@ -62,6 +62,25 @@ static void write_linked_location(CURL *curl, const char *location,
size_t loclen, FILE *stream);
#endif
int tool_write_headers(struct HdrCbData *hdrcbdata, FILE *stream)
{
struct curl_slist *h = hdrcbdata->headlist;
int rc = 1;
while(h) {
/* not "handled", just show it */
size_t len = strlen(h->data);
if(len != fwrite(h->data, 1, len, stream))
goto fail;
h = h->next;
}
rc = 0; /* success */
fail:
curl_slist_free_all(hdrcbdata->headlist);
hdrcbdata->headlist = NULL;
return rc;
}
/*
** callback for CURLOPT_HEADERFUNCTION
*/
@ -164,63 +183,90 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
* Content-Disposition header specifying a filename property.
*/
else if(hdrcbdata->honor_cd_filename &&
(cb > 20) && checkprefix("Content-disposition:", str)) {
const char *p = str + 20;
else if(hdrcbdata->honor_cd_filename) {
if((cb > 20) && checkprefix("Content-disposition:", str)) {
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;
if(memcmp(p, "filename=", 9)) {
/* no match, find next parameter */
while((p < end) && *p && (*p != ';'))
while((p < end) && *p && !ISALPHA(*p))
p++;
if((p < end) && *p)
continue;
else
if(p > end - 9)
break;
}
p += 9;
len = cb - (size_t)(p - str);
filename = parse_filename(p, len);
if(filename) {
if(outs->stream) {
/* indication of problem, get out! */
free(filename);
return CURL_WRITEFUNC_ERROR;
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;
if(per->config->output_dir) {
outs->filename = aprintf("%s/%s", per->config->output_dir,
filename);
free(filename);
if(!outs->filename)
len = cb - (size_t)(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))
return CURL_WRITEFUNC_ERROR;
if(tool_write_headers(&per->hdrcbdata, outs->stream))
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))
return CURL_WRITEFUNC_ERROR;
break;
}
break;
if(!outs->stream && !tool_create_output_file(outs, per->config))
return CURL_WRITEFUNC_ERROR;
if(tool_write_headers(&per->hdrcbdata, outs->stream))
return CURL_WRITEFUNC_ERROR;
} /* content-disposition handling */
if(hdrcbdata->honor_cd_filename &&
hdrcbdata->config->show_headers) {
/* still awaiting the Content-Disposition header, store the header in
memory. Since it is not zero terminated, we need an extra dance. */
char *clone = aprintf("%.*s", (int)cb, (char *)str);
if(clone) {
struct curl_slist *old = hdrcbdata->headlist;
hdrcbdata->headlist = curl_slist_append(old, clone);
free(clone);
if(!hdrcbdata->headlist) {
curl_slist_free_all(old);
return CURL_WRITEFUNC_ERROR;
}
}
else {
curl_slist_free_all(hdrcbdata->headlist);
hdrcbdata->headlist = NULL;
return CURL_WRITEFUNC_ERROR;
}
return cb; /* done for now */
}
if(!outs->stream && !tool_create_output_file(outs, per->config))
return CURL_WRITEFUNC_ERROR;
}
}
if(hdrcbdata->config->writeout) {

View file

@ -46,9 +46,12 @@ struct HdrCbData {
struct OutStruct *outs;
struct OutStruct *heads;
struct OutStruct *etag_save;
struct curl_slist *headlist;
bool honor_cd_filename;
};
int tool_write_headers(struct HdrCbData *hdrcbdata, FILE *stream);
/*
** callback for CURLOPT_HEADERFUNCTION
*/

View file

@ -345,7 +345,13 @@ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata)
}
else
#endif
{
if(per->hdrcbdata.headlist) {
if(tool_write_headers(&per->hdrcbdata, outs->stream))
return CURL_WRITEFUNC_ERROR;
}
rc = fwrite(buffer, sz, nmemb, outs->stream);
}
if(bytes == rc)
/* we added this amount of data to the output */

View file

@ -2763,9 +2763,7 @@ ParameterError parse_args(struct GlobalConfig *global, int argc,
}
if(!result && config->content_disposition) {
if(config->show_headers)
result = PARAM_CONTDISP_SHOW_HEADER;
else if(config->resume_from_current)
if(config->resume_from_current)
result = PARAM_CONTDISP_RESUME_FROM;
}

View file

@ -342,7 +342,6 @@ typedef enum {
PARAM_NO_PREFIX,
PARAM_NUMBER_TOO_LARGE,
PARAM_NO_NOT_BOOLEAN,
PARAM_CONTDISP_SHOW_HEADER, /* --include and --remote-header-name */
PARAM_CONTDISP_RESUME_FROM, /* --continue-at and --remote-header-name */
PARAM_READ_ERROR,
PARAM_EXPAND_ERROR, /* --expand problem */

View file

@ -67,8 +67,6 @@ const char *param2text(ParameterError error)
return "too large number";
case PARAM_NO_NOT_BOOLEAN:
return "used '--no-' for option that is not a boolean";
case PARAM_CONTDISP_SHOW_HEADER:
return "showing headers and --remote-header-name cannot be combined";
case PARAM_CONTDISP_RESUME_FROM:
return "--continue-at and --remote-header-name cannot be combined";
case PARAM_READ_ERROR:

View file

@ -770,7 +770,8 @@ skip:
free(per->uploadfile);
if(global->parallel)
free(per->errorbuffer);
curl_slist_free_all(per->hdrcbdata.headlist);
per->hdrcbdata.headlist = NULL;
return result;
}