diff --git a/exec/emailfiles.js b/exec/emailfiles.js
index 712f044aa1d10fde38d9d5e3ede0caa89dc160a6..5b5f2a95b4dcace4e4b910c119ee043aa200cef8 100755
--- a/exec/emailfiles.js
+++ b/exec/emailfiles.js
@@ -143,7 +143,7 @@ function sendfiles()
 			, to_net_type: NET_INTERNET
 			, to_net_addr: address
 			, attr: MSG_NOREPLY // Suppress bounce messages
-			, netattr: MSG_KILLSENT
+			, netattr: NETMSG_KILLSENT
 			, auxattr: MSG_FILEATTACH
 		};
 		var msgbody = format(options.msgbody || "Your requested file (%u of %u) is attached." +
diff --git a/exec/imapservice.js b/exec/imapservice.js
index a760bc2d4a91d8e2ea1bc29b8fe8956c0c4e7d05..0942c64df1a1e0147d13626d43d98ca5fe2bfaea 100644
--- a/exec/imapservice.js
+++ b/exec/imapservice.js
@@ -1033,31 +1033,31 @@ function parse_flags(inflags)
 				break;
 
 			case 'LOCAL':
-				flags.netattr |= MSG_LOCAL;
+				flags.netattr |= NETMSG_LOCAL;
 				break;
 			case 'INTRANSIT':
-				flags.netattr |= MSG_INTRANSIT;
+				flags.netattr |= NETMSG_INTRANSIT;
 				break;
 			case 'SENT':
-				flags.netattr |= MSG_SENT;
+				flags.netattr |= NETMSG_SENT;
 				break;
 			case 'KILLSENT':
-				flags.netattr |= MSG_KILLSENT;
+				flags.netattr |= NETMSG_KILLSENT;
 				break;
 			case 'ARCHIVESENT':
-				flags.netattr |= MSG_ARCHIVESENT;
+				flags.netattr |= NETMSG_ARCHIVESENT;
 				break;
 			case 'HOLD':
-				flags.netattr |= MSG_HOLD;
+				flags.netattr |= NETMSG_HOLD;
 				break;
 			case 'CRASH':
-				flags.netattr |= MSG_CRASH;
+				flags.netattr |= NETMSG_CRASH;
 				break;
 			case 'IMMEDIATE':
-				flags.netattr |= MSG_IMMEDIATE;
+				flags.netattr |= NETMSG_IMMEDIATE;
 				break;
 			case 'DIRECT':
-				flags.netattr |= MSG_DIRECT;
+				flags.netattr |= NETMSG_DIRECT;
 				break;
 		}
 	}
@@ -1123,21 +1123,21 @@ function calc_msgflags(attr, netattr, num, msg, readonly)
 	if(attr & MSG_NOREPLY)
 		flags += "NOREPLY ";
 
-	if(netattr & MSG_INTRANSIT)
+	if(netattr & NETMSG_INTRANSIT)
 		flags += "INTRANSIT ";
-	if(netattr & MSG_SENT)
+	if(netattr & NETMSG_SENT)
 		flags += "SENT ";
-	if(netattr & MSG_KILLSENT)
+	if(netattr & NETMSG_KILLSENT)
 		flags += "KILLSENT ";
-	if(netattr & MSG_ARCHIVESENT)
+	if(netattr & NETMSG_ARCHIVESENT)
 		flags += "ARCHIVESENT ";
-	if(netattr & MSG_HOLD)
+	if(netattr & NETMSG_HOLD)
 		flags += "HOLD ";
-	if(netattr & MSG_CRASH)
+	if(netattr & NETMSG_CRASH)
 		flags += "CRASH ";
-	if(netattr & MSG_IMMEDIATE)
+	if(netattr & NETMSG_IMMEDIATE)
 		flags += "IMMEDIATE ";
-	if(netattr & MSG_DIRECT)
+	if(netattr & NETMSG_DIRECT)
 		flags += "DIRECT ";
 
 	if(attr==0xffff || orig_ptrs[num] < msg) {
diff --git a/exec/load/smbdefs.js b/exec/load/smbdefs.js
index 7a93f8302a9d664d28eca280776f29d4567fdfeb..87074140aeadb80818913a389deda50c69ab87ab 100644
--- a/exec/load/smbdefs.js
+++ b/exec/load/smbdefs.js
@@ -80,15 +80,15 @@ const POLL_RESULTS_SHIFT	= 30;
 
 
 /* Message network attributes */
-const MSG_LOCAL				= (1<<0);	// Msg created locally
-const MSG_INTRANSIT			= (1<<1);	// Msg is in-transit
-const MSG_SENT				= (1<<2);	// Sent to remote
-const MSG_KILLSENT			= (1<<3);	// Kill when sent
-const MSG_ARCHIVESENT 		= (1<<4);	// Archive when sent
-const MSG_HOLD				= (1<<5);	// Hold for pick-up
-const MSG_CRASH				= (1<<6);	// Crash
-const MSG_IMMEDIATE			= (1<<7);	// Send Msg now, ignore restrictions
-const MSG_DIRECT			= (1<<8);	// Send directly to destination
+const NETMSG_LOCAL				= (1<<0);	// Msg created locally
+const NETMSG_INTRANSIT			= (1<<1);	// Msg is in-transit
+const NETMSG_SENT				= (1<<2);	// Sent to remote
+const NETMSG_KILLSENT			= (1<<3);	// Kill when sent
+const NETMSG_ARCHIVESENT 		= (1<<4);	// Archive when sent
+const NETMSG_HOLD				= (1<<5);	// Hold for pick-up
+const NETMSG_CRASH				= (1<<6);	// Crash
+const NETMSG_IMMEDIATE			= (1<<7);	// Send Msg now, ignore restrictions
+const NETMSG_DIRECT				= (1<<8);	// Send directly to destination
 
 /* Net types */
 const NET_NONE				= 0;		// Local message
diff --git a/exec/msglist.js b/exec/msglist.js
index af9e309fa5f1f661b07d44327e6dc709a2336624..a25da1a451df4a4871889a65064bfb43380e3d30 100644
--- a/exec/msglist.js
+++ b/exec/msglist.js
@@ -713,14 +713,14 @@ function mail_reply(msg, reply_all)
 	return success;
 }
 
-function download_msg(msg, plain_text)
+function download_msg(msg, msgbase, plain_text)
 {
 	var fname = system.temp_dir + "msg_" + msg.number + ".txt";
 	var f = new File(fname);
 	if(!f.open("wb"))
 		return false;
 	var text = msgbase.get_msg_body(msg
-				,/* strip ctrl-a */false
+				,/* strip ctrl-a */plain_text
 				,/* dot-stuffing */false
 				,/* tails */true
 				,plain_text);
@@ -731,6 +731,30 @@ function download_msg(msg, plain_text)
 	return bbs.send_file(fname);
 }
 
+function download_thread(thread_id, msgbase, plain_text)
+{
+	var fname = system.temp_dir + "thread_" + thread_id + ".txt";
+	var f = new File(fname);
+	if(!f.open("wb"))
+		return false;
+	const total_msgs = msgbase.total_msgs;
+	for (var i = 0; i < total_msgs; ++i) {
+		var msg = msgbase.get_msg_header(true, i);
+		if (msg == null || msg.thread_id !== thread_id)
+			continue;
+		var text = msgbase.get_msg_body(msg
+				,/* strip ctrl-a */plain_text
+				,/* dot-stuffing */false
+				,/* tails */true
+				,plain_text);
+		f.write(msg.get_rfc822_header(/* force_update: */false, /* unfold: */false
+			,/* default_content_type */!plain_text));
+		f.writeln(text);
+	}
+	f.close();
+	return bbs.send_file(fname);
+}
+
 function content_description(msg)
 {
 	var desc = [];
@@ -1132,7 +1156,11 @@ function list_msgs(msgbase, list, current, preview, grp_name, sub_name)
 							break;
 						case 'D':
 							console.clearline();
-							if(!console.noyes("Download message", P_NOCRLF)) {
+							if(list[current].thread_id && !console.noyes("Download thread", P_NOCRLF)) {
+								if(!download_thread(list[current].thread_id, msgbase, console.yesno("Plain-text only")))
+									alert("failed");
+							}
+							else if(!console.noyes("Download message", P_NOCRLF)) {
 								if(!download_msg(list[current], msgbase, console.yesno("Plain-text only")))
 									alert("failed");
 							}
@@ -1489,9 +1517,9 @@ function msg_attributes(msg, msgbase, short)
 	if(msg.attr&MSG_VALIDATED)						result.push(options.attr_valid || "Valid"), str += 'V';
 	if(msg.attr&MSG_PRIVATE)						result.push(options.attr_private || "Priv"), str += 'p';
 	if(msg.attr&MSG_POLL)							result.push(options.attr_poll || "Poll"), str += '?';
-	if(msg.netattr&MSG_SENT)						result.push(options.attr_sent || "Sent"), str += 's';
-	if(msg.netattr&MSG_KILLSENT)					result.push(options.attr_sent || "KS"), str += 'k';
-	if(msg.netattr&MSG_INTRANSIT)					result.push(options.attr_intransit || "InTransit"), str += 'T';
+	if(msg.netattr&NETMSG_SENT)						result.push(options.attr_sent || "Sent"), str += 's';
+	if(msg.netattr&NETMSG_KILLSENT)					result.push(options.attr_sent || "KS"), str += 'k';
+	if(msg.netattr&NETMSG_INTRANSIT)				result.push(options.attr_intransit || "InTransit"), str += 'T';
 	/*
 	if(sub_op(subnum) && msg->hdr.attr&MSG_ANONYMOUS)	return 'A';
 	*/
diff --git a/exec/notransit.js b/exec/notransit.js
index 200aaea7fd3aa32868fca76f0647a746acbd8495..5da207aa9249e7709eccea343e698f5c3d170a5a 100644
--- a/exec/notransit.js
+++ b/exec/notransit.js
@@ -16,8 +16,8 @@ for(i=0;i<total_msgs;i++) {
 								/* offset:			*/	i,
 								/* expand_fields:	*/	false);
 	printf("#%lu from: %-30s %08lx\r\n",hdr.number,hdr.from,hdr.netattr);
-	if(hdr && hdr.netattr&MSG_INTRANSIT) {
-		hdr.netattr&=~MSG_INTRANSIT;
+	if(hdr && hdr.netattr&NETMSG_INTRANSIT) {
+		hdr.netattr&=~NETMSG_INTRANSIT;
 		printf("Removing in-transit attribute from message #%lu\r\n",hdr.number);
 		if(!mail.put_msg_header(true,i,hdr))
 			alert(mail.last_error);