From 6e2476ea4d72d91de83c481b1e019d93d6317b35 Mon Sep 17 00:00:00 2001
From: Kalle Olavi Niemitalo <kon@iki.fi>
Date: Tue, 30 Sep 2008 10:06:20 +0300
Subject: [PATCH] Bug 1053: Fix crash when download ends.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

ELinks attempted to display a message box on file_download.term, but
it had already closed that terminal and freed the struct terminal.  To
fix this, reset file_download.term pointers to NULL when the terminal
is about to be destroyed.  Also, assert in download_data_store() that
file_download.term is either NULL or in the global "terminals" list.

Reported by أحمد المحمودي.
---
 NEWS                    |  8 ++++++++
 src/session/download.c  | 26 ++++++++++++++++++++++++++
 src/session/download.h  |  1 +
 src/terminal/terminal.c | 19 +++++++++++++++++++
 src/terminal/terminal.h |  6 ++++++
 5 files changed, 60 insertions(+)

diff --git a/NEWS b/NEWS
index 87e6590c8..65406688e 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,14 @@ You can see the complete list of recent changes, bugfixes and new features
 in the http://repo.or.cz/w/elinks.git[gitweb interface]. See the ChangeLog
 file for details.
 
+ELinks 0.11.5.GIT now:
+----------------------
+
+To be released as 0.11.6.
+
+* critical bug 1053: fix crash if a download finishes after ELinks has
+  closed the terminal from which the download was started
+
 ELinks 0.11.5:
 --------------
 
diff --git a/src/session/download.c b/src/session/download.c
index a34a6f829..afce521de 100644
--- a/src/session/download.c
+++ b/src/session/download.c
@@ -220,6 +220,29 @@ destroy_downloads(struct session *ses)
 	}
 }
 
+void
+detach_downloads_from_terminal(struct terminal *term)
+{
+	struct file_download *file_download, *next;
+
+	assert(term != NULL);
+	if_assert_failed return;
+
+	foreachsafe (file_download, next, downloads) {
+		if (file_download->term != term)
+			continue;
+
+		if (!file_download->external_handler) {
+			file_download->term = NULL;
+			if (file_download->ses
+			    && file_download->ses->tab->term == term)
+				file_download->ses = NULL;
+			continue;
+		}
+
+		abort_download(file_download);
+	}
+}
 
 static void
 download_error_dialog(struct file_download *file_download, int saved_errno)
@@ -307,6 +330,9 @@ download_data_store(struct download *download, struct file_download *file_downlo
 {
 	struct terminal *term = file_download->term;
 
+	assert_terminal_ptr_not_dangling(term);
+	if_assert_failed term = file_download->term = NULL;
+
 	if (!term) {
 		/* No term here, so no beep. --Zas */
 		abort_download(file_download);
diff --git a/src/session/download.h b/src/session/download.h
index 1bcc4aadf..b27cd3b78 100644
--- a/src/session/download.h
+++ b/src/session/download.h
@@ -107,6 +107,7 @@ void create_download_file(struct terminal *, unsigned char *, unsigned char **,
 
 void abort_all_downloads(void);
 void destroy_downloads(struct session *);
+void detach_downloads_from_terminal(struct terminal *);
 
 int setup_download_handler(struct session *, struct download *, struct cache_entry *, int);
 
diff --git a/src/terminal/terminal.c b/src/terminal/terminal.c
index 895e89806..b22158223 100644
--- a/src/terminal/terminal.c
+++ b/src/terminal/terminal.c
@@ -115,6 +115,7 @@ destroy_terminal(struct terminal *term)
 #ifdef CONFIG_BOOKMARKS
 	bookmark_auto_save_tabs(term);
 #endif
+	detach_downloads_from_terminal(term);
 
 	while (!list_empty(term->windows))
 		delete_window(term->windows.next);
@@ -197,6 +198,24 @@ unblock_terminal(struct terminal *term)
 		textarea_edit(1, NULL, NULL, NULL, NULL);
 }
 
+#ifndef CONFIG_FASTMEM
+void
+assert_terminal_ptr_not_dangling(const struct terminal *suspect)
+{
+	struct terminal *term;
+
+	if (suspect == NULL)
+		return;
+
+	foreach (term, terminals) {
+		if (term == suspect)
+			return;
+	}
+
+	assertm(0, "Dangling pointer to struct terminal");
+}
+#endif /* !CONFIG_FASTMEM */
+
 void
 exec_on_terminal(struct terminal *term, unsigned char *path,
 		 unsigned char *delete, int fg)
diff --git a/src/terminal/terminal.h b/src/terminal/terminal.h
index 0689f098e..8b44ff1a9 100644
--- a/src/terminal/terminal.h
+++ b/src/terminal/terminal.h
@@ -150,6 +150,12 @@ void destroy_all_terminals(void);
 void exec_thread(unsigned char *, int);
 void close_handle(void *);
 
+#ifdef CONFIG_FASTMEM
+#define assert_terminal_ptr_not_dangling(suspect) ((void) 0)
+#else  /* assert() does something */
+void assert_terminal_ptr_not_dangling(const struct terminal *);
+#endif
+
 #define TERM_FN_TITLE	1
 #define TERM_FN_RESIZE	2