tool_formparse: rewrite the headers file parser

The -F option allows users to provide a file with a set of headers for a
specific formpost section. This code used old handcrafted parsing logic
that potentially could do wrong.

Rewrite to use my_get_line() and dynbuf. Supports longer lines and
should be more solid parsing code.

Gets somewhat complicated by the (unwise) feature that allows "folding"
of header lines in the file: if a line starts with a space it should be
appended to the previous.

The previous code trimmed spurious CR characters wherever they would
occur in a line but this version does not. It does not seem like
something we want or that users would expect.

Test 646 uses this feature.
Closes #19113
This commit is contained in:
Daniel Stenberg 2025-10-18 11:58:36 +02:00
parent f32451c12b
commit f847d2ed02
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
2 changed files with 53 additions and 54 deletions

View File

@ -28,6 +28,7 @@
#include "tool_getparam.h"
#include "tool_paramhlp.h"
#include "tool_formparse.h"
#include "tool_parsecfg.h"
#include "memdebug.h" /* keep this as LAST include */
@ -417,65 +418,63 @@ static int slist_append(struct curl_slist **plist, const char *data)
}
/* Read headers from a file and append to list. */
static int read_field_headers(const char *filename, FILE *fp,
struct curl_slist **pheaders)
static int read_field_headers(FILE *fp, struct curl_slist **pheaders)
{
size_t hdrlen = 0;
size_t pos = 0;
bool incomment = FALSE;
int lineno = 1;
char hdrbuf[999] = ""; /* Max. header length + 1. */
struct dynbuf line;
bool error = FALSE;
int err = 0;
for(;;) {
int c = getc(fp);
if(c == EOF || (!pos && !ISSPACE(c))) {
/* Strip and flush the current header. */
while(hdrlen && ISSPACE(hdrbuf[hdrlen - 1]))
hdrlen--;
if(hdrlen) {
hdrbuf[hdrlen] = '\0';
if(slist_append(pheaders, hdrbuf)) {
errorf("Out of memory for field headers");
return -1;
}
hdrlen = 0;
curlx_dyn_init(&line, 8092);
while(my_get_line(fp, &line, &error)) {
const char *ptr = curlx_dyn_ptr(&line);
size_t len = curlx_dyn_len(&line);
bool folded = FALSE;
if(ptr[0] == '#') /* comment */
continue;
else if(ptr[0] == ' ') /* a continuation from the line before */
folded = TRUE;
/* trim off trailing CRLFs and whitespaces */
while(len && (ISNEWLINE(ptr[len -1]) || ISBLANK(ptr[len - 1])))
len--;
if(!len)
continue;
curlx_dyn_setlen(&line, len); /* set the new length */
if(folded && *pheaders) {
/* append this new line onto the previous line */
struct dynbuf amend;
struct curl_slist *l = *pheaders;
curlx_dyn_init(&amend, 8092);
/* find the last node */
while(l && l->next)
l = l->next;
/* add both parts */
if(curlx_dyn_add(&amend, l->data) || curlx_dyn_addn(&amend, ptr, len)) {
err = -1;
break;
}
curl_free(l->data);
/* we use maprintf here to make it a libcurl alloc, to match the previous
curl_slist_append */
l->data = curl_maprintf("%s", curlx_dyn_ptr(&amend));
curlx_dyn_free(&amend);
if(!l->data) {
errorf("Out of memory for field headers");
err = 1;
}
}
switch(c) {
case EOF:
if(ferror(fp)) {
char errbuf[STRERROR_LEN];
errorf("Header file %s read error: %s", filename,
curlx_strerror(errno, errbuf, sizeof(errbuf)));
return -1;
}
return 0; /* Done. */
case '\r':
continue; /* Ignore. */
case '\n':
pos = 0;
incomment = FALSE;
lineno++;
continue;
case '#':
if(!pos)
incomment = TRUE;
else {
err = slist_append(pheaders, ptr);
}
if(err) {
errorf("Out of memory for field headers");
err = -1;
break;
}
pos++;
if(!incomment) {
if(hdrlen == sizeof(hdrbuf) - 1) {
warnf("File %s line %d: header too long (truncated)",
filename, lineno);
c = ' ';
}
if(hdrlen <= sizeof(hdrbuf) - 1)
hdrbuf[hdrlen++] = (char) c;
}
}
/* NOTREACHED */
curlx_dyn_free(&line);
return err;
}
static int get_param_part(char endchar,
@ -572,7 +571,7 @@ static int get_param_part(char endchar,
curlx_strerror(errno, errbuf, sizeof(errbuf)));
}
else {
int i = read_field_headers(hdrfile, fp, &headers);
int i = read_field_headers(fp, &headers);
curlx_fclose(fp);
if(i) {

View File

@ -42,7 +42,7 @@ It may contain any type of data.
X-fileheader1: This is a header from a file
# This line is another comment. It precedes a folded header.
X-fileheader2: This is #a
X-fileheader2: This is #a
folded header
</file2>
</client>