[PATCH] Correctly format <form enctype=text/plain> as key=value\r\n

наб <[email protected]> Thu, 25 Sep 2025 16:53:48 +0200
Newsgroups gmane.comp.web.lynx.devel
Message-ID <jhkxx2vkr6murtvzysvb2loqj5pie4zea4gs4mwa5lnprztrox@tarta.nabijaczleweli.xyz>
<https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#submit-body>:
	text/plain
		Let pairs be the result of converting to a list of name-value pairs with entry list.
		Let body be the result of running the text/plain encoding algorithm with pairs.
		Set body to the result of encoding body using encoding.
		Let mimeType be `text/plain`.

<https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#text/plain-encoding-algorithm>:
	4.10.22.9 Plain text form data

	The text/plain encoding algorithm,
	given a list of name-value pairs pairs, is as follows:
		Let result be the empty string.
		For each pair in pairs:
			Append pair's name to result.
			Append a single U+003D EQUALS SIGN character (=) to result.
			Append pair's value to result.
			Append a U+000D CARRIAGE RETURN (CR)
			         U+000A LINE FEED (LF) character pair to result.
		Return result.

	Payloads using the text/plain format are intended to be
	human readable. They are not reliably interpretable by
	computer, as the format is ambiguous (for example, there
	is no way to distinguish a literal newline in a value from
	the newline at the end of the value).

Currently, lynx encodes the form data like
	vendor=
	http

	type=
	tar
the correct result (and what we see with this patch) is
	vendor=http\r
	type=tar\r

These all convert trivially, but F_TEXTAREA_TYPE is delimited varyingly,
in that I think you can get
	textarea=line1
	line2\r
	continuation1
	continuation2\r
? but this is not expected to be reproducible per WHATWG,
and the ambiguity was already the case.

Fixes https://bugs.debian.org/1116319
---
 src/GridText.c | 44 ++++++++++++++++++--------------------------
 1 file changed, 18 insertions(+), 26 deletions(-)

diff --git a/src/GridText.c b/src/GridText.c
index 419f6ee..1a4c6f8 100644
--- a/src/GridText.c
+++ b/src/GridText.c
@@ -11171,8 +11171,7 @@ int HText_SubmitForm(FormInfo * submit_item, DocInfo *doc,
     if (submit_item->submit_enctype &&
 	!strncasecomp(submit_item->submit_enctype, STR_PLAINTEXT, 10)) {
 	/*
-	 * Do not hex escape, and use physical newlines
-	 * to separate name=value pairs.  -FM
+	 * Do not hex escape, dump key=value\r\n directly.
 	 */
 	PlainText = TRUE;
     } else if (submit_item->submit_enctype &&
@@ -11776,7 +11775,7 @@ int HText_SubmitForm(FormInfo * submit_item, DocInfo *doc,
 			first_one = FALSE;
 		    } else {
 			if (PlainText) {
-			    BStrCat0(my_query, "\n");
+			    /* no additional delimiter beside the \r\n  */
 			} else if (SemiColon) {
 			    BStrCat0(my_query, ";");
 			} else if (Boundary) {
@@ -11810,12 +11809,11 @@ int HText_SubmitForm(FormInfo * submit_item, DocInfo *doc,
 						 my_data[anchor_count].quote);
 
 		HTBprintf(&my_query,
-			  "%s%s%s%s%s",
+			  "%s%s%s%s",
 			  escaped1,
 			  (Boundary ? "" : "="),
-			  (PlainText ? "\n" : ""),
 			  escaped2,
-			  ((PlainText && *escaped2) ? "\n" : ""));
+			  (PlainText ? "\r\n" : ""));
 		break;
 	    case F_CHECKBOX_TYPE:
 	    case F_RADIO_TYPE:
@@ -11827,12 +11825,11 @@ int HText_SubmitForm(FormInfo * submit_item, DocInfo *doc,
 						 my_data[anchor_count].quote);
 
 		HTBprintf(&my_query,
-			  "%s%s%s%s%s",
+			  "%s%s%s%s",
 			  escaped1,
 			  (Boundary ? "" : "="),
-			  (PlainText ? "\n" : ""),
 			  escaped2,
-			  ((PlainText && *escaped2) ? "\n" : ""));
+			  (PlainText ? "\r\n" : ""));
 		break;
 	    case F_SUBMIT_TYPE:
 	    case F_TEXT_SUBMIT_TYPE:
@@ -11873,11 +11870,11 @@ int HText_SubmitForm(FormInfo * submit_item, DocInfo *doc,
 				  "%s.x=0%s%s.y=0%s",
 				  escaped1,
 				  (PlainText ?
-				   "\n" : (SemiColon ?
+				   "\r\n" : (SemiColon ?
 					   ";" : "&")),
 				  escaped1,
 				  ((PlainText && *escaped1) ?
-				   "\n" : ""));
+				   "\r\n" : ""));
 		    }
 		} else {
 		    /*
@@ -11885,12 +11882,11 @@ int HText_SubmitForm(FormInfo * submit_item, DocInfo *doc,
 		     * pair.  = FM
 		     */
 		    HTBprintf(&my_query,
-			      "%s%s%s%s%s",
+			      "%s%s%s%s",
 			      escaped1,
 			      (Boundary ? "" : "="),
-			      (PlainText ? "\n" : ""),
 			      escaped2,
-			      ((PlainText && *escaped2) ? "\n" : ""));
+			      (PlainText ? "\r\n" : ""));
 		}
 		break;
 	    case F_RESET_TYPE:
@@ -11917,18 +11913,15 @@ int HText_SubmitForm(FormInfo * submit_item, DocInfo *doc,
 						    MultipartContentType);
 
 		    HTBprintf(&my_query,
-			      "%s%s%s%s%s",
+			      "%s%s%s%s",
 			      escaped1,
 			      (Boundary ? "" : "="),
-			      (PlainText ? "\n" : ""),
 			      escaped2,
-			      ((PlainText && *escaped2) ? "\n" : ""));
+			      (PlainText ? "\r\n" : ""));
 		} else {
-		    const char *marker = (PlainText
-					  ? "\n"
-					  : (Boundary
-					     ? "\r\n"
-					     : "%0d%0a"));
+		    const char *marker = ((PlainText || Boundary)
+					  ? "\r\n"
+					  : "%0d%0a");
 
 		    /*
 		     * This is a continuation of a previous textarea.
@@ -11979,10 +11972,9 @@ int HText_SubmitForm(FormInfo * submit_item, DocInfo *doc,
 		}
 
 		HTBprintf(&my_query,
-			  "%s%s%s",
+			  "%s%s",
 			  escaped1,
-			  (Boundary ? "" : "="),
-			  (PlainText ? "\n" : ""));
+			  (Boundary ? "" : "="));
 		/*
 		 * If we have anything more than the trailing null we added,
 		 * append the file-data to the query.
@@ -11992,7 +11984,7 @@ int HText_SubmitForm(FormInfo * submit_item, DocInfo *doc,
 			     BStrData(my_data[anchor_count].data),
 			     BStrLen(my_data[anchor_count].data) - 1);
 		    if (PlainText)
-			HTBprintf(&my_query, "\n");
+			HTBprintf(&my_query, "\r\n");
 		}
 		break;
 #endif /* USE_FILE_UPLOAD */
-- 
2.39.5
signature.asc (application/pgp-signature, 833 B)
-----BEGIN PGP SIGNATURE-----

iQIzBAABCgAdFiEEfWlHToQCjFzAxEFjvP0LAY0mWPEFAmjVV3sACgkQvP0LAY0m
WPHTNg//TyxaJt8wkruETHT+o6F+J17bSf2+pQ96f7fTlxKJLQybCPk1ZHh1+jQA
9SWWjn55zfE8atqtnaUbd3wKYpH6dLhwa5lPLx2oNzKrC3a+lvCFbhvs49Ise7KH
pHF4tt2WLvwDQbR6JhFCmXFnR9gZOpvzSGG3DOoKtERYjuicL84GS1ZghSSkP4+6
aA8Mvf80RNh6j/0gd1QXZs+EXCIkfWUR2jMkPLI6Z+G2jgZ2olFRlh9oIVUGuw+i
XEczFj2svlCtZdmcLgKcRsuWrnuCnhz6NERFOEuukkCC3F1MsHFQIG35/iGlGdTi
oEc97psBJ9D+ar8BGqrk9GT6jOSrlrmK/dBfNZAKrsB//pjfnsvzKAuN3rhweznY
N+XqxPk1rIKXihm+dajLM28q45dyvZ7jGpzULlsQDAYvrTMQlZhTA4pUd2Tduknl
F0kOlPkLBQ+3N9yYIrGuipVCXyddLDpfxZTeiHARlKF0IPG/QEyt9u6uw3ZNO66S
LoICCBY4hEaXv+wBA/9ZQ4JguRX/F1d55r8lcLaQaZVCvzOsxbcV3hHGs79XKvWp
Yw2wEAN1nyI2cjbSY11vFFovijlMZBuaXUT+F2k/atHye8AvICRM6F0XshfH97Dx
vL9x0u8pXuZ0dtN/8FfIMfyiFG6YZIDvJ+qx63c90/LigxjLI8Y=
=SoN2
-----END PGP SIGNATURE-----