[PATCH] Fixed bug relating to newlines in hidden input fields

Peter Collingbourne <[email protected]> Mon, 3 Nov 2008 21:56:49 +0000
Newsgroups gmane.comp.web.links
Message-ID <[email protected]>
This patch fixes an issue whereby a newline character appearing within
a hidden input field is incorrectly reinterpreted as a space character.
The patch handles almost all cases, and includes a test case.
15/18 tests pass, but the remainder currently fail due to the fact
that ELinks does not currently support textarea scripting.
---
 AUTHORS                          |    1 +
 src/document/html/parser/forms.c |    4 +-
 src/document/html/parser/parse.c |    6 +-
 src/document/html/parser/parse.h |    5 ++
 src/viewer/text/form.c           |   30 +++++++++
 src/viewer/text/form.h           |    1 +
 src/viewer/text/textarea.c       |   13 +----
 test/server/crlf.conf            |    2 +
 test/server/crlf.py              |  121 ++++++++++++++++++++++++++++++++++++++
 9 files changed, 168 insertions(+), 15 deletions(-)
 create mode 100644 test/server/crlf.conf
 create mode 100755 test/server/crlf.py

diff --git a/AUTHORS b/AUTHORS
index 866f0d7..18e8641 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -457,6 +457,7 @@ Peder Stray <[email protected]>
 	Fix handling of key presses turning up as key prefixes
 
 Peter Collingbourne <[email protected]>
+	Fixed bug relating to newlines in hidden input fields
 	Fixed compiler errors and warnings in src/util/random.c where CONFIG_SSL undefined
 
 Peter Gervai <[email protected]>
diff --git a/src/document/html/parser/forms.c b/src/document/html/parser/forms.c
index c34459a..5f9dc47 100644
--- a/src/document/html/parser/forms.c
+++ b/src/document/html/parser/forms.c
@@ -287,7 +287,9 @@ html_input(struct html_context *html_context, unsigned char *a,
 		mem_free(al);
 	}
 
-	if (fc->type != FC_FILE)
+	if (fc->type == FC_HIDDEN)
+		fc->default_value = get_lit_attr_val(a, "value", cp);
+	else if (fc->type != FC_FILE)
 		fc->default_value = get_attr_val(a, "value", cp);
 	if (!fc->default_value) {
 		if (fc->type == FC_CHECKBOX)
diff --git a/src/document/html/parser/parse.c b/src/document/html/parser/parse.c
index 128485e..399c4e6 100644
--- a/src/document/html/parser/parse.c
+++ b/src/document/html/parser/parse.c
@@ -179,9 +179,11 @@ next_attr:
 
 /* parse_quoted_value: */
 			while (*(++e) != quote) {
-				if (*e == ASCII_CR) continue;
 				if (!*e) goto parse_error;
-				if (*e != ASCII_TAB && *e != ASCII_LF)
+				if (flags & HTML_ATTR_LITERAL_NL)
+					add_chr(attr, attrlen, *e);
+				else if (*e == ASCII_CR) continue;
+				else if (*e != ASCII_TAB && *e != ASCII_LF)
 					add_chr(attr, attrlen, *e);
 				else if (!(flags & HTML_ATTR_EAT_NL))
 					add_chr(attr, attrlen, ' ');
diff --git a/src/document/html/parser/parse.h b/src/document/html/parser/parse.h
index 4eaa154..9498e43 100644
--- a/src/document/html/parser/parse.h
+++ b/src/document/html/parser/parse.h
@@ -25,6 +25,10 @@ enum html_attr_flags {
 	/* If HTML_ATTR_NO_CONV is set, then convert_string() is not called
 	 * on value. Unused for now. */
 	/* HTML_ATTR_NO_CONV = 4, */
+
+	/* If HTML_ATTR_LITERAL_NL is set, carriage return, newline and tab
+	 * characters are returned literally. */
+	HTML_ATTR_LITERAL_NL = 8,
 };
 
 /* Parses html element attributes.
@@ -37,6 +41,7 @@ unsigned char *get_attr_value(register unsigned char *e, unsigned char *name, in
 
 /* Wrappers for get_attr_value(). */
 #define get_attr_val(e, name, cp) get_attr_value(e, name, cp, HTML_ATTR_NONE)
+#define get_lit_attr_val(e, name, cp) get_attr_value(e, name, cp, HTML_ATTR_LITERAL_NL)
 #define get_url_val(e, name, cp) get_attr_value(e, name, cp, HTML_ATTR_EAT_NL)
 #define has_attr(e, name, cp) (!!get_attr_value(e, name, cp, HTML_ATTR_TEST))
 
diff --git a/src/viewer/text/form.c b/src/viewer/text/form.c
index f2b7b9d..e64ae88 100644
--- a/src/viewer/text/form.c
+++ b/src/viewer/text/form.c
@@ -807,6 +807,30 @@ get_successful_controls(struct document_view *doc_view,
 	sort_submitted_values(list);
 }
 
+unsigned char *
+encode_crlf(struct submitted_value *sv)
+{
+	struct string newtext;
+	int i;
+
+	assert(sv && sv->value);
+	if_assert_failed return NULL;
+
+	if (!init_string(&newtext)) return NULL;
+
+	for (i = 0; sv->value[i]; i++) {
+		if (sv->value[i] == '\r') {
+			if (sv->value[i+1] != '\n')
+				add_crlf_to_string(&newtext);
+		} else if (sv->value[i] == '\n')
+			add_crlf_to_string(&newtext);
+		else
+			add_char_to_string(&newtext, sv->value[i]);
+	}
+
+	return newtext.source;
+}
+
 static void
 encode_controls(LIST_OF(struct submitted_value) *l, struct string *data,
 		int cp_from, int cp_to)
@@ -850,6 +874,8 @@ encode_controls(LIST_OF(struct submitted_value) *l, struct string *data,
 
 			p2 = convert_string(convert_table, sv->value,
 					    strlen(sv->value), -1, CSM_FORM, NULL, NULL, NULL);
+		} else if (sv->type == FC_HIDDEN) {
+			p2 = encode_crlf(sv);
 		} else {
 			p2 = stracpy(sv->value);
 		}
@@ -1120,6 +1146,10 @@ encode_text_plain(LIST_OF(struct submitted_value) *l, struct string *data,
 			value = area51 = encode_textarea(sv);
 			if (!area51) break;
 			/* Fall through */
+		case FC_HIDDEN:
+			if (!area51) value = area51 = encode_crlf(sv);
+			if (!area51) break;
+			/* Fall through */
 		case FC_TEXT:
 		case FC_PASSWORD:
 			/* Convert back to original encoding (see
diff --git a/src/viewer/text/form.h b/src/viewer/text/form.h
index 74a9991..79757ba 100644
--- a/src/viewer/text/form.h
+++ b/src/viewer/text/form.h
@@ -99,6 +99,7 @@ struct submitted_value {
 struct submitted_value *init_submitted_value(unsigned char *name, unsigned char *value, enum form_type type, struct form_control *fc, int position);
 void done_submitted_value(struct submitted_value *sv);
 void done_submitted_value_list(LIST_OF(struct submitted_value) *list);
+unsigned char *encode_crlf(struct submitted_value *sv);
 
 struct uri *get_form_uri(struct session *ses, struct document_view *doc_view, struct form_control *fc);
 
diff --git a/src/viewer/text/textarea.c b/src/viewer/text/textarea.c
index 60a0c60..7749f7b 100644
--- a/src/viewer/text/textarea.c
+++ b/src/viewer/text/textarea.c
@@ -488,9 +488,7 @@ unsigned char *
 encode_textarea(struct submitted_value *sv)
 {
 	struct form_control *fc;
-	struct string newtext;
 	void *blabla;
-	int i;
 
 	assert(sv && sv->value);
 	if_assert_failed return NULL;
@@ -503,16 +501,7 @@ encode_textarea(struct submitted_value *sv)
 	blabla = format_text(sv->value, fc->cols, fc->wrap, 1);
 	mem_free_if(blabla);
 
-	if (!init_string(&newtext)) return NULL;
-
-	for (i = 0; sv->value[i]; i++) {
-		if (sv->value[i] != '\n')
-			add_char_to_string(&newtext, sv->value[i]);
-		else
-			add_crlf_to_string(&newtext);
-	}
-
-	return newtext.source;
+	return encode_crlf(sv);
 }
 
 
diff --git a/test/server/crlf.conf b/test/server/crlf.conf
new file mode 100644
index 0000000..33d61a2
--- /dev/null
+++ b/test/server/crlf.conf
@@ -0,0 +1,2 @@
+set document.browse.forms.confirm_submit = 0
+set ecmascript.enable = 1
diff --git a/test/server/crlf.py b/test/server/crlf.py
new file mode 100755
index 0000000..84b49fd
--- /dev/null
+++ b/test/server/crlf.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+
+import os
+from BaseHTTPServer import *
+import tempfile
+import signal
+
+r, w = os.pipe()
+
+C_CR = 0
+C_LF = 1
+C_CRLF = 2
+
+E_Raw = 0
+E_Entity = 1
+E_JavaScript = 2
+
+F_Hidden = 0
+F_TextArea = 1
+
+def encode(ch, encoding):
+	if ch == C_CRLF:
+		return encode(C_CR, encoding) + encode(C_LF, encoding)
+	if ch == C_CR:
+		if encoding == E_Raw:
+			return "\r"
+		if encoding == E_JavaScript:
+			return "\\r"
+		if encoding == E_Entity:
+			return "&#013;"
+	if ch == C_LF:
+		if encoding == E_Raw:
+			return "\n"
+		if encoding == E_JavaScript:
+			return "\\n"
+		if encoding == E_Entity:
+			return "&#010;"
+
+def get_form(ch, encoding, field):
+	text = "foo" + encode(ch, encoding) + "bar"
+	if encoding == E_JavaScript:
+		text_initial = ""
+	else:
+		text_initial = text
+
+	s = """<html>
+<head>
+<title>Form Test</title>
+</head>
+<body>
+<form id="form1" name="form1" action="http://127.0.0.1:8090/">
+"""
+	if field == F_Hidden:
+		s += '<input type="hidden" id="field1" name="field1" value="' + text_initial + '">'
+	elif field == F_TextArea:
+		s += '<textarea id="field1" name="field1">' + text_initial + '</textarea>'
+	s += "\n</form>"
+	if encoding == E_JavaScript:
+		s += """
+<script>
+document.form1.field1.value = '%s';
+</script>""" % (text)
+
+	s += "</body></html>"
+	return s
+
+class forwarder(BaseHTTPRequestHandler):
+	def do_GET(self):
+		w.write(self.path + "\n")
+		w.flush()
+		self.send_response(200)
+		self.send_header("Content-Type", "text/plain")
+		self.end_headers()
+		self.wfile.write("Dummy response")
+
+def runtest(r, *args):
+	form = get_form(*args)
+
+	tmpfile, tmpname = tempfile.mkstemp(".html")
+	tmpfile = os.fdopen(tmpfile, 'w')
+	tmpfile.write(form)
+	tmpfile.close()
+
+	linkspid = os.spawnlp(os.P_NOWAIT, 'elinks', 'elinks',
+		'-config-dir', os.getcwd(),
+		'-config-file', 'crlf.conf',
+		'-no-connect', '1',
+		'-auto-submit', '1',
+		tmpname)
+	path = r.readline()
+	os.kill(linkspid, signal.SIGINT)
+	os.waitpid(linkspid, 0)
+
+	os.unlink(tmpname)
+
+	return path
+
+pid = os.fork()
+
+if pid:
+	os.close(w)
+	r = os.fdopen(r)
+
+	paths = []
+
+	for c in [C_CR, C_LF, C_CRLF]:
+		for e in [E_Raw, E_Entity, E_JavaScript]:
+			for f in [F_Hidden, F_TextArea]:
+				paths.append(("%d %d %d " % (c, e, f)) + runtest(r, c, e, f))
+
+	for path in paths:
+		print path,
+
+	os.kill(pid, signal.SIGTERM)
+	os.waitpid(pid, 0)
+else:
+	os.close(r)
+	w = os.fdopen(w, 'w')
+	server_address = ('127.0.0.1', 8090)
+	httpd = HTTPServer(server_address, forwarder)
+	httpd.serve_forever()
-- 
1.5.6.5


-- 
Peter

_______________________________________________
elinks-dev mailing list
[email protected]
http://linuxfromscratch.org/mailman/listinfo/elinks-dev
signature.asc (application/pgp-signature, 189 B)
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)

iD8DBQFJD3OeXDqn6DSH7HERAntqAJ0a9jJEkOeUmYZUgbzdsadi2xQuDwCg0xDp
uz7SFTOjU1hlZ2FKFIAswgk=
=m/aq
-----END PGP SIGNATURE-----