From 658b9cc70f337c6be75945e1d16dbefeb9dd9643 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Mon, 3 Nov 2008 21:56:49 +0000 Subject: [PATCH 1/3] Fixed bug relating to newlines in hidden input fields 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 | 3 + 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, 170 insertions(+), 15 deletions(-) create mode 100644 test/server/crlf.conf create mode 100755 test/server/crlf.py diff --git a/AUTHORS b/AUTHORS index 0b1b0f27..3d86ced1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -449,6 +449,9 @@ Pavol Babincak Peder Stray Fix handling of key presses turning up as key prefixes +Peter Collingbourne + Fixed bug relating to newlines in hidden input fields + Peter Gervai Manpage updates Mailcap to links.cfg convertor diff --git a/src/document/html/parser/forms.c b/src/document/html/parser/forms.c index c34459a1..5f9dc475 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 2fefb801..87d3b592 100644 --- a/src/document/html/parser/parse.c +++ b/src/document/html/parser/parse.c @@ -177,9 +177,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 41263226..81597de8 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 3d5738e0..b21fe7ac 100644 --- a/src/viewer/text/form.c +++ b/src/viewer/text/form.c @@ -795,6 +795,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) @@ -838,6 +862,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); } @@ -1134,6 +1160,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 74a99915..79757bac 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 b7b0634c..0f3f8eec 100644 --- a/src/viewer/text/textarea.c +++ b/src/viewer/text/textarea.c @@ -490,9 +490,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; @@ -505,16 +503,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 00000000..33d61a2d --- /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 00000000..84b49fd1 --- /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 " " + if ch == C_LF: + if encoding == E_Raw: + return "\n" + if encoding == E_JavaScript: + return "\\n" + if encoding == E_Entity: + return " " + +def get_form(ch, encoding, field): + text = "foo" + encode(ch, encoding) + "bar" + if encoding == E_JavaScript: + text_initial = "" + else: + text_initial = text + + s = """ + +Form Test + + +
+""" + if field == F_Hidden: + s += '' + elif field == F_TextArea: + s += '' + s += "\n
" + if encoding == E_JavaScript: + s += """ +""" % (text) + + s += "" + 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() From 5d540e1f95edd690df874d1d6ea15b2ec770d185 Mon Sep 17 00:00:00 2001 From: Kalle Olavi Niemitalo Date: Sun, 9 Nov 2008 23:36:35 +0200 Subject: [PATCH 2/3] NEWS entry for newlines in hidden fields --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index 944a31a5..11ed89bc 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,8 @@ ELinks 0.12pre2.GIT now: To be released as 0.12pre3, 0.12rc1, or even 0.12.0. This branch also includes the changes listed under ``ELinks 0.11.5.GIT'' below. +* Preserve newlines in hidden input fields, and submit them as CRLF. + Previously, they could turn into spaces or disappear entirely. * Perl scripts can use modules that dynamically load C libraries, like XML::LibXML::SAX does. * enhancement: Updated ISO 8859-7, ISO 8859-16, KOI8-R, and MacRoman. From e2cbb796346cb7d6899ed358f9c9e2d74607cba9 Mon Sep 17 00:00:00 2001 From: Kalle Olavi Niemitalo Date: Mon, 10 Nov 2008 00:02:44 +0200 Subject: [PATCH 3/3] AUTHORS: Peter Collingbourne allows relicensing Peter Collingbourne posted to elinks-dev on 3 November 2008: >> ELinks is currently licensed under GPLv2 only. I hope we can >> eventually change the licence to also allow later versions of >> GPL and permit linking with OpenSSL. Do you allow such >> relicensing for your patch? > > I agree to this. According to COPYING, such permissions should be listed in AUTHORS. --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 3d86ced1..6569ebc7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -450,6 +450,7 @@ Peder Stray Fix handling of key presses turning up as key prefixes Peter Collingbourne + Allows relicensing under GPLv2-or-later and linking with OpenSSL Fixed bug relating to newlines in hidden input fields Peter Gervai