From 64f0f6e7a87fa07da8bf8bf0200c289d1c2ae596 Mon Sep 17 00:00:00 2001 From: Kalle Olavi Niemitalo Date: Sun, 17 Aug 2008 17:36:43 +0300 Subject: [PATCH 1/5] Don't link with libgnutls-openssl, which is now GPLv3+. ELinks used to call the MD5 code in libgnutls-openssl, part of GNUTLS-EXTRA, which was licensed under GNU GPL version 2 or later. In GnuTLS 2.2.0 however, the license of GNUTLS-EXTRA has been changed to GNU GPL version 3 or later. This is no longer compatible with GNU GPL version 2 as used in the current ELinks, because GPLv2 clause 2. b) requires the whole work to be licensed under GPLv2, and GPLv3 does not allow that. If anyone is still using a pre-2.2 GnuTLS, he or she can tweak configure.in to check the version or just assume it's old enough. There is not much reason to do so though, as including the MD5 code in ELinks seems to cost only about 4 kilobytes on i686. (cherry picked from commit 9ca0182ec6ffa5f652a3d45529e82ee31329f301) --- NEWS | 2 ++ configure.in | 10 ++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 74f1019d3..bd53c68a5 100644 --- a/NEWS +++ b/NEWS @@ -223,6 +223,8 @@ To be released as 0.11.5. * bug 698: Attach controls to the intended form even if it is incorrectly nested in a table. (Was broken in 0.11.4.) * build bug 1021: fixed uninitialized variable in http_got_header +* build: don't use libgnutls-openssl, which is no longer GPLv2 + compatible in GnuTLS 2.2.0 ELinks 0.11.4: -------------- diff --git a/configure.in b/configure.in index 31acfea96..3e2a88148 100644 --- a/configure.in +++ b/configure.in @@ -1090,12 +1090,10 @@ else CFLAGS="$CFLAGS_X" AC_SUBST(GNUTLS_CFLAGS) - # Verify if the MD5 compatibility layer is usable. - CONFIG_GNUTLS_OPENSSL_COMPAT=yes - EL_CHECK_OPTIONAL_LIBRARY(CONFIG_GNUTLS_OPENSSL_COMPAT, - [GNU TLS OpenSSL compatibility], - gnutls/openssl.h, gnutls-openssl, - MD5_Init) + # GnuTLS 2.2.0 changed libgnutls-openssl from GPLv2+ + # to GPLv3+. Don't link that with the GPLv2 ELinks. + # ELinks will use its internal MD5 code instead. + CONFIG_GNUTLS_OPENSSL_COMPAT=no else if test -n "$gnutls_withval" && test "x$gnutls_withval" != xno; then AC_MSG_ERROR([GNUTLS (1.2 or later) not found. ELinks no longer supports GNUTLS 1.1.]) From 49f529a582077895f3a6b3236294f6ed55e9896d Mon Sep 17 00:00:00 2001 From: Kalle Olavi Niemitalo Date: Sun, 17 Aug 2008 22:08:36 +0300 Subject: [PATCH 2/5] 1044: Check for -rdynamic with libraries. With Sun Studio 11 on Solaris 9, we get "cc: Warning: illegal option -dynamic"; then, cc proceeds anyway, but the option can prevent the linker from finding the libraries listed in -l operands. To detect this, move the -rdynamic check in configure.in down to a place where the libraries have already been added to $LDFLAGS. So if -rdynamic interferes with the search for libraries, ELinks won't use it. Merely moving the test would also change the location of -rdynamic in $LDFLAGS. Counteract that by making the test add -rdynamic to the beginning of $LDFLAGS, rather than to the end. This may make the test more reliable on Solaris. --- NEWS | 2 ++ configure.in | 79 ++++++++++++++++++++++++++++------------------------ 2 files changed, 45 insertions(+), 36 deletions(-) diff --git a/NEWS b/NEWS index bd53c68a5..845a696f8 100644 --- a/NEWS +++ b/NEWS @@ -30,6 +30,8 @@ includes the changes listed under "ELinks 0.11.4.GIT now" below. SSL errors especially in HTTP POST requests using GnuTLS. * minor bug 951: SpiderMonkey scripting objects used to prevent ELinks from removing files from the memory cache. +* build bug 1044: Check whether -rdynamic works with libraries. + With Sun Studio 11 on Solaris 9, it reportedly doesn't. Bugs that should be removed from NEWS before the 0.12.0 release: diff --git a/configure.in b/configure.in index 3e2a88148..d00d8d277 100644 --- a/configure.in +++ b/configure.in @@ -250,42 +250,6 @@ EL_CHECK_CODE([variadic macros], HAVE_VARIADIC_MACROS, #define a(b,c...) printf(b,##c)], [a("foo");a("%s","bar");a("%s%s","baz","quux");]) -# Check for -rdynamic -# -# gcc -rdynamic calls ld -export-dynamic, which adds all symbols of -# the executable to its dynamic symbol table. ELinks uses this for -# two purposes: -# -# 1. If ELinks detects a bug, it can try to display a backtrace by -# calling backtrace_symbols_fd() in the GNU libc. The glibc-2.3.6 -# manual states users of GNU ld must pass -rdynamic to make the -# symbols available to the program. -# -# 2. It would eventually be nice to dynamically load shared -# libraries as plugins (bug 73). The plugins must be able to -# call ELinks functions. This can be implemented either by -# registering all callable functions in ELinks-specific data -# structures, or by letting the dynamic linker handle them. -# The latter way requires something equivalent to -rdynamic. -# -# Because backtraces are not needed for correct operation, and bug -# 73 is not yet being fixed, the configure script and makefiles -# should not complain to the user if they find that -rdynamic does -# not work. Besides, it was reported at elinks-users on 2006-09-12 -# that gcc-3.4.2 with "ld: Software Generation Utilities - Solaris -# Link Editors: 5.8-1.284" on Sun Solaris 8 Sparc does not support -# -rdynamic but does something equivalent automatically. (This was -# tested with "nm -D elinks | grep redraw_from_window".) -# -# FIXME: This check doesn't work. Something to do with the compiler -# happily ignoring it and stderr not being checked for error messages. -AC_MSG_CHECKING([for -rdynamic]) -LDFLAGS_X="$LDFLAGS" -LDFLAGS="$LDFLAGS -rdynamic" -AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[have_rdynamic=yes],[have_rdynamic=no]) -test "$have_rdynamic" = no && LDFLAGS="$LDFLAGS_X" -AC_MSG_RESULT($have_rdynamic) - # =================================================================== # Check for POSIX # =================================================================== @@ -1357,12 +1321,55 @@ AC_ARG_ENABLE(weehoofooboomookerchoo, [AC_MSG_ERROR(Are you strange, or what?)]) +# =================================================================== +# Further LDFLAGS tweaks +# =================================================================== + # == EMX hack test "$CONFIG_OS_OS2" = yes && LDFLAGS="$LDFLAGS -Zexe" test "$CONFIG_OS_OS2" = yes && LDFLAGS=`echo "$LDFLAGS" | sed "s/-Zbin-files//g"` +# Check for -rdynamic +# +# gcc -rdynamic calls ld -export-dynamic, which adds all symbols of +# the executable to its dynamic symbol table. ELinks uses this for +# two purposes: +# +# 1. If ELinks detects a bug, it can try to display a backtrace by +# calling backtrace_symbols_fd() in the GNU libc. The glibc-2.3.6 +# manual states users of GNU ld must pass -rdynamic to make the +# symbols available to the program. +# +# 2. It would eventually be nice to dynamically load shared +# libraries as plugins (bug 73). The plugins must be able to +# call ELinks functions. This can be implemented either by +# registering all callable functions in ELinks-specific data +# structures, or by letting the dynamic linker handle them. +# The latter way requires something equivalent to -rdynamic. +# +# Because backtraces are not needed for correct operation, and bug +# 73 is not yet being fixed, the configure script and makefiles +# should not complain to the user if they find that -rdynamic does +# not work. Besides, it was reported at elinks-users on 2006-09-12 +# that gcc-3.4.2 with "ld: Software Generation Utilities - Solaris +# Link Editors: 5.8-1.284" on Sun Solaris 8 Sparc does not support +# -rdynamic but does something equivalent automatically. (This was +# tested with "nm -D elinks | grep redraw_from_window".) +# +# With Sun Studio 11 on Solaris 9, we get "cc: Warning: illegal option +# -dynamic"; then, cc proceeds anyway, but the option can prevent the +# linker from finding the libraries listed in -l operands. So this +# -rdynamic check needs to happen after the libraries have already +# been added to $LDFLAGS. +AC_MSG_CHECKING([for -rdynamic]) +LDFLAGS_X="$LDFLAGS" +LDFLAGS="-rdynamic $LDFLAGS" +AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[have_rdynamic=yes],[have_rdynamic=no]) +test "$have_rdynamic" = no && LDFLAGS="$LDFLAGS_X" +AC_MSG_RESULT($have_rdynamic) + # =================================================================== # Export directory paths # =================================================================== From 298f3af6c61055b5bc2dacfdf8726ab61519bea6 Mon Sep 17 00:00:00 2001 From: Witold Filipczyk Date: Sun, 10 Aug 2008 10:30:34 +0200 Subject: [PATCH 3/5] 1041: Do not show the "Error parsing" on ftp://sunsite.icm.edu.pl/. --- src/protocol/ftp/ftp.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/protocol/ftp/ftp.c b/src/protocol/ftp/ftp.c index ca5b6f4a8..b3cc8c7a4 100644 --- a/src/protocol/ftp/ftp.c +++ b/src/protocol/ftp/ftp.c @@ -1328,7 +1328,14 @@ ftp_process_dirlist(struct cache_entry *cached, off_t *pos, } } else { - ERROR("Error parsing: [%.*s]", line_length, buf); + struct string string; + + if (!init_string(&string)) return ret; + add_bytes_to_string(&string, buf, line_length); + add_char_to_string(&string, '\n'); + add_fragment(cached, *pos, string.source, string.length); + *pos += string.length; + done_string(&string); } } } From b7d3b4f687d7ac5720de511b615f42b47441d80f Mon Sep 17 00:00:00 2001 From: Kalle Olavi Niemitalo Date: Thu, 4 Sep 2008 11:21:06 +0300 Subject: [PATCH 4/5] 1041: Add ftp_add_unparsed_line: HTML entities and more error checks. Separate the formatting of unparsed lines from ftp_process_dirlist() to a new function ftp_add_unparsed_line(). Check for all possible out-of-memory errors. Encode HTML metacharacters as entity references and document how charsets are handled FTP directory listings. Add a NEWS entry. --- NEWS | 2 ++ src/protocol/ftp/ftp.c | 64 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/NEWS b/NEWS index 845a696f8..66adcced5 100644 --- a/NEWS +++ b/NEWS @@ -28,6 +28,8 @@ includes the changes listed under "ELinks 0.11.4.GIT now" below. enabled it at connection.ssl.cert_verify. * bug 1040: Blacklist servers that don't support TLS. This reduces SSL errors especially in HTTP POST requests using GnuTLS. +* bugs 1007, 1041: Display unrecognized lines in FTP directory + listings, instead of annoying the user with error messages. * minor bug 951: SpiderMonkey scripting objects used to prevent ELinks from removing files from the memory cache. * build bug 1044: Check whether -rdynamic works with libraries. diff --git a/src/protocol/ftp/ftp.c b/src/protocol/ftp/ftp.c index b3cc8c7a4..92de3a600 100644 --- a/src/protocol/ftp/ftp.c +++ b/src/protocol/ftp/ftp.c @@ -1125,10 +1125,25 @@ ftp_got_final_response(struct socket *socket, struct read_buffer *rb) } -/* How to format an FTP directory listing in HTML. */ +/** How to format an FTP directory listing in HTML. */ struct ftp_dir_html_format { + /** Codepage used by C library functions such as strftime(). + * If the FTP server sends non-ASCII bytes in file names or + * such, ELinks normally passes them straight through to the + * generated HTML, which will eventually be parsed using the + * codepage specified in the document.codepage.assume option. + * However, when ELinks itself generates strings with + * strftime(), it turns non-ASCII bytes into entity references + * based on libc_codepage, to make sure they become the right + * characters again. */ int libc_codepage; + + /** Nonzero if directories should be displayed in a different + * color. From the document.browse.links.color_dirs option. */ int colorize_dir; + + /** The color of directories, in "#rrggbb" format. This is + * initialized and used only if colorize_dir is nonzero. */ unsigned char dircolor[8]; }; @@ -1290,7 +1305,41 @@ ftp_get_line(struct cache_entry *cached, unsigned char *buf, int bufl, return -1; } -/* List a directory in html format. */ +/** Generate HTML for a line that was received from the FTP server but + * could not be parsed. The caller is supposed to have added a \
+ * start tag.  (At the time of writing, init_directory_listing() was
+ * used for that.)
+ *
+ * @return -1 if out of memory, or 0 if successful.  */
+static int
+ftp_add_unparsed_line(struct cache_entry *cached, off_t *pos, int *tries, 
+		      const unsigned char *line, int line_length)
+{
+	int our_ret;
+	struct string string;
+	int frag_ret;
+
+	our_ret = -1;	 /* assume out of memory if returning early */
+	if (!init_string(&string)) goto out;
+	if (!add_html_to_string(&string, line, line_length)) goto out;
+	if (!add_char_to_string(&string, '\n')) goto out;
+
+	frag_ret = add_fragment(cached, *pos, string.source, string.length);
+	if (frag_ret == -1) goto out;
+	*pos += string.length;
+	if (frag_ret == 1) *tries = 0;
+
+	our_ret = 0;		/* success */
+
+out:
+	done_string(&string);	/* safe even if init_string failed */
+	return our_ret;
+}
+
+/** List a directory in html format.
+ *
+ * @return the number of bytes used from the beginning of @a buffer,
+ * or -1 if out of memory.  */
 static int
 ftp_process_dirlist(struct cache_entry *cached, off_t *pos,
 		    unsigned char *buffer, int buflen, int last,
@@ -1328,14 +1377,11 @@ ftp_process_dirlist(struct cache_entry *cached, off_t *pos,
 			}
 
 		} else {
-			struct string string;
+			int retv = ftp_add_unparsed_line(cached, pos, tries,
+							 buf, line_length);
 
-			if (!init_string(&string)) return ret;
-			add_bytes_to_string(&string, buf, line_length);
-			add_char_to_string(&string, '\n');
-			add_fragment(cached, *pos, string.source, string.length);
-			*pos += string.length;
-			done_string(&string);
+			if (retv == -1) /* out of memory; propagate to caller */
+				return retv;
 		}
 	}
 }

From 7de8b9940c96f51c55c2706ed9aa6ad11257d7ee Mon Sep 17 00:00:00 2001
From: Kalle Olavi Niemitalo 
Date: Thu, 4 Sep 2008 11:35:07 +0300
Subject: [PATCH 5/5] Bug 1013: Fix type mismatches in gopher.c

When I replaced enum connection_state with struct connection_state,
I missed parts of src/protocol/gopher/gopher.c.  Finish the change there.
---
 src/protocol/gopher/gopher.c | 52 ++++++++++++++++++++----------------
 1 file changed, 29 insertions(+), 23 deletions(-)

diff --git a/src/protocol/gopher/gopher.c b/src/protocol/gopher/gopher.c
index 7fb8f8edd..2a0c6526b 100644
--- a/src/protocol/gopher/gopher.c
+++ b/src/protocol/gopher/gopher.c
@@ -208,7 +208,7 @@ add_gopher_command(struct connection *conn, struct string *command,
 	int querylen;
 
 	if (!init_string(command))
-		return S_OUT_OF_MEM;
+		return connection_state(S_OUT_OF_MEM);
 
 	/* Look for search string */
 	query = memchr(selector, '?', selectorlen);
@@ -251,7 +251,7 @@ add_gopher_command(struct connection *conn, struct string *command,
 #if 0
 			return init_gopher_cso_cache_entry(conn);
 #endif
-			return S_GOPHER_CSO_ERROR;
+			return connection_state(S_GOPHER_CSO_ERROR);
 		}
 
 		add_uri_decoded(command, selector, selectorlen, 0);
@@ -266,7 +266,7 @@ add_gopher_command(struct connection *conn, struct string *command,
 
 	add_crlf_to_string(command);
 
-	return S_CONN;
+	return connection_state(S_CONN);
 }
 
 static struct connection_state
@@ -303,7 +303,7 @@ init_gopher_connection_info(struct connection *conn)
 	}
 
 	state = add_gopher_command(conn, &command, entity, selector, selectorlen);
-	if (state != S_CONN)
+	if (!is_in_state(state, S_CONN))
 		return state;
 
 	/* Atleast the command should contain \r\n to ask the server
@@ -314,7 +314,7 @@ init_gopher_connection_info(struct connection *conn)
 	gopher = mem_calloc(1, size);
 	if (!gopher) {
 		done_string(&command);
-		return S_OUT_OF_MEM;
+		return connection_state(S_OUT_OF_MEM);
 	}
 
 	gopher->entity = entity_info;
@@ -325,7 +325,7 @@ init_gopher_connection_info(struct connection *conn)
 
 	conn->info = gopher;
 
-	return S_CONN;
+	return connection_state(S_CONN);
 }
 
 
@@ -580,11 +580,11 @@ read_gopher_directory_data(struct connection *conn, struct read_buffer *rb)
 		struct connection_state state;
 
 		state = init_directory_listing(&buffer, conn->uri);
-		if (state != S_OK)
+		if (!is_in_state(state, S_OK))
 			return state;
 
 	} else if (!init_string(&buffer)) {
-		return S_OUT_OF_MEM;
+		return connection_state(S_OUT_OF_MEM);
 	}
 
 	while ((end = get_gopher_line_end(rb->data, rb->length))) {
@@ -592,7 +592,7 @@ read_gopher_directory_data(struct connection *conn, struct read_buffer *rb)
 
 		/* Break on line with a dot by itself */
 		if (!line) {
-			state = S_OK;
+			state = connection_state(S_OK);
 			break;
 		}
 
@@ -601,7 +601,8 @@ read_gopher_directory_data(struct connection *conn, struct read_buffer *rb)
 		kill_buffer_data(rb, end - rb->data);
 	}
 
-	if (state != S_TRANS || conn->socket->state == SOCKET_CLOSED)
+	if (!is_in_state(state, S_TRANS)
+	    || conn->socket->state == SOCKET_CLOSED)
 		add_to_string(&buffer,
 			"
\n" "\n" @@ -646,7 +647,7 @@ init_gopher_index_cache_entry(struct connection *conn) if (!init_gopher_cache_entry(conn) || !init_string(&buffer)) - return S_OUT_OF_MEM; + return connection_state(S_OUT_OF_MEM); where = get_uri_string(conn->uri, URI_PUBLIC); @@ -679,7 +680,9 @@ init_gopher_index_cache_entry(struct connection *conn) conn->cached->content_type = stracpy("text/html"); - return conn->cached->content_type ? S_OK : S_OUT_OF_MEM; + return conn->cached->content_type + ? connection_state(S_OK) + : connection_state(S_OUT_OF_MEM); } @@ -688,12 +691,12 @@ read_gopher_response_data(struct socket *socket, struct read_buffer *rb) { struct connection *conn = socket->conn; struct gopher_connection_info *gopher = conn->info; - struct connection_state state = S_TRANS; + struct connection_state state = connection_state(S_TRANS); assert(gopher && gopher->entity); if (!conn->cached && !init_gopher_cache_entry(conn)) { - abort_connection(conn, S_OUT_OF_MEM); + abort_connection(conn, connection_state(S_OUT_OF_MEM)); return; } @@ -709,7 +712,7 @@ read_gopher_response_data(struct socket *socket, struct read_buffer *rb) /* FIXME: Merge CSO support */ state = read_gopher_cso_data(conn, rb); #endif - state = S_GOPHER_CSO_ERROR; + state = connection_state(S_GOPHER_CSO_ERROR); break; case GOPHER_SOUND: @@ -739,15 +742,16 @@ read_gopher_response_data(struct socket *socket, struct read_buffer *rb) /* Has the transport layer forced a shut down? */ if (socket->state == SOCKET_CLOSED) { - state = S_OK; + state = connection_state(S_OK); } - if (state != S_TRANS) { + if (!is_in_state(state, S_TRANS)) { abort_connection(conn, state); return; } - read_from_socket(conn->socket, rb, S_TRANS, read_gopher_response_data); + read_from_socket(conn->socket, rb, connection_state(S_TRANS), + read_gopher_response_data); } @@ -758,7 +762,8 @@ send_gopher_command(struct socket *socket) struct gopher_connection_info *gopher = conn->info; request_from_socket(socket, gopher->command, gopher->commandlen, - S_SENT, SOCKET_END_ONCLOSE, read_gopher_response_data); + connection_state(S_SENT), SOCKET_END_ONCLOSE, + read_gopher_response_data); } @@ -767,7 +772,7 @@ void gopher_protocol_handler(struct connection *conn) { struct uri *uri = conn->uri; - struct connection_state state = S_CONN; + struct connection_state state = connection_state(S_CONN); switch (get_uri_port(uri)) { case 105: @@ -777,7 +782,8 @@ gopher_protocol_handler(struct connection *conn) * - FM */ if (uri->datalen == 1 && *uri->data == GOPHER_CSO) { /* FIXME: redirect_cache() */ - abort_connection(conn, S_GOPHER_CSO_ERROR); + abort_connection(conn, + connection_state(S_GOPHER_CSO_ERROR)); } break; @@ -790,14 +796,14 @@ gopher_protocol_handler(struct connection *conn) * - FM */ if (uri->datalen >= 1 && *uri->data == GOPHER_FILE) { /* FIXME: redirect_cache() */ - abort_connection(conn, S_OK); + abort_connection(conn, connection_state(S_OK)); } #endif break; } state = init_gopher_connection_info(conn); - if (state != S_CONN) { + if (!is_in_state(state, S_CONN)) { /* FIXME: Handle bad selector ... */ abort_connection(conn, state); return;