]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
tool_formparse: rewrite the headers file parser
authorDaniel Stenberg <daniel@haxx.se>
Sat, 18 Oct 2025 09:58:36 +0000 (11:58 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Sat, 18 Oct 2025 10:49:53 +0000 (12:49 +0200)
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

src/tool_formparse.c
tests/data/test646

index 0f0962ce0d1f6255a57a7f22f1ed5d18828f1aac..004ad6ff4c70b7e9e6dfd352dfadfd2d756f808d 100644 (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. */
-
-  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;
-      }
-    }
-
-    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++;
+  struct dynbuf line;
+  bool error = FALSE;
+  int err = 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;
-    case '#':
-      if(!pos)
-        incomment = TRUE;
-      break;
-    }
+    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--;
 
-    pos++;
-    if(!incomment) {
-      if(hdrlen == sizeof(hdrbuf) - 1) {
-        warnf("File %s line %d: header too long (truncated)",
-              filename, lineno);
-        c = ' ';
+    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;
       }
-      if(hdrlen <= sizeof(hdrbuf) - 1)
-        hdrbuf[hdrlen++] = (char) c;
+      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;
+      }
+    }
+    else {
+      err = slist_append(pheaders, ptr);
+    }
+    if(err) {
+      errorf("Out of memory for field headers");
+      err = -1;
+      break;
     }
   }
-  /* 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) {
index 4f26dbdf9291ab3d47b8a72d5c559f66ff3dd579..fe86e32775b560b8170f8ce912d2bc64a554eaf4 100644 (file)
@@ -42,7 +42,7 @@ It may contain any type of data.
 X-fileheader1: This is a header from a file \r
 
 # This line is another comment. It precedes a folded header.
-X-fileheader2: This is \r#a
+X-fileheader2: This is #a
  folded header
 </file2>
 </client>