From 51c917d043aa2d0d85fed78d0e1c6ff0490ea84c Mon Sep 17 00:00:00 2001
From: "Rob Swindell (on Windows 11)" <rob@synchro.net>
Date: Tue, 19 Nov 2024 13:20:37 -0800
Subject: [PATCH] Allow Telnet client to be in binary mode persistently

Before this change, we'd always request a return to Telnet NVT (turn off
binary-TX in both directions) after any file transfer. So although a Telnet
session might be negotiated into binary mode shortly after connection
(e.g., using "telnet -8"), it would be reverted back to NVT mode after any
file transfer.

The request to turn off remote binary-TX after executing external programs
didn't actually accomplish anything since we track the Telnet option states
internally and don't send redundant requests (e.g. the change into a mode
we're already in). External programs aren't expected to send Telnet requests
anyway, so I think this was some holdover from early days of stp/sexyz
development.
---
 src/sbbs3/download.cpp | 18 +++++++++++-------
 src/sbbs3/sbbs.h       |  9 +++++++--
 src/sbbs3/telgate.cpp  |  5 ++++-
 src/sbbs3/xtrn.cpp     |  6 ------
 4 files changed, 22 insertions(+), 16 deletions(-)

diff --git a/src/sbbs3/download.cpp b/src/sbbs3/download.cpp
index f2f697380d..9c7e4d0d63 100644
--- a/src/sbbs3/download.cpp
+++ b/src/sbbs3/download.cpp
@@ -104,21 +104,25 @@ const char* sbbs_t::protcmdline(prot_t* prot, enum XFER_TYPE type)
 	return("invalid transfer type");
 }
 
-void sbbs_t::data_transfer_begin(void)
+bool sbbs_t::data_transfer_begin()
 {
 	sys_status|=SS_FILEXFER;	/* disable spy during file xfer */
+	bool telnet_was_nvt = telnet_is_nvt();
 	/* enable telnet binary transmission in both directions */
 	request_telnet_opt(TELNET_DO,TELNET_BINARY_TX);
 	request_telnet_opt(TELNET_WILL,TELNET_BINARY_TX);
 	console |= CON_RAW_IN;
+	return telnet_was_nvt;
 }
 
-void sbbs_t::data_transfer_end(void)
+void sbbs_t::data_transfer_end(bool telnet_was_nvt)
 {
 	sys_status&=~SS_FILEXFER;
-	/* Got back to Text/NVT mode */
-	request_telnet_opt(TELNET_DONT,TELNET_BINARY_TX);
-	request_telnet_opt(TELNET_WONT,TELNET_BINARY_TX);
+	if(telnet_was_nvt) {
+		/* Got back to Text/NVT mode */
+		request_telnet_opt(TELNET_DONT,TELNET_BINARY_TX);
+		request_telnet_opt(TELNET_WONT,TELNET_BINARY_TX);
+	}
 	console &= ~CON_RAW_IN;
 }
 
@@ -164,11 +168,11 @@ int sbbs_t::protocol(prot_t* prot, enum XFER_TYPE type
 	cmdline=cmdstr(protcmdline(prot,type), fpath, fspec, NULL, ex_mode);
 	SAFEPRINTF(msg,"Transferring %s",cmdline);
 	spymsg(msg);
-	data_transfer_begin();
+	bool was_nvt_mode = data_transfer_begin();
 	time_t start = time(NULL);
 	i=external(cmdline,ex_mode,p);
 	time_t end = time(NULL);
-	data_transfer_end();
+	data_transfer_end(was_nvt_mode);
 
 	// Save DSZLOG to logfile
 	if((stream=fnopen(NULL,protlog,O_RDONLY))!=NULL) {
diff --git a/src/sbbs3/sbbs.h b/src/sbbs3/sbbs.h
index f3be3a703a..7e45978efa 100644
--- a/src/sbbs3/sbbs.h
+++ b/src/sbbs3/sbbs.h
@@ -539,6 +539,11 @@ public:
 	int 	telnet_rows = 0;
 	int		telnet_cols = 0;
 	int		telnet_speed = 0;
+	bool	telnet_is_nvt() {
+		return !(telnet_mode & TELNET_MODE_OFF)
+			&& telnet_local_option[TELNET_BINARY_TX] != TELNET_DO
+			&& telnet_remote_option[TELNET_BINARY_TX] != TELNET_WILL;
+	}
 
 	xpevent_t	telnet_ack_event;
 
@@ -1169,8 +1174,8 @@ public:
 	bool	bulkupload(int dirnum);
 
 	/* download.cpp */
-	void	data_transfer_begin(void);
-	void	data_transfer_end(void);
+	bool	data_transfer_begin(void);
+	void	data_transfer_end(bool was_telnet_nvt_mode);
 	void	downloadedfile(file_t* f);
 	void	notdownloaded(off_t size, time_t elapsed);
 	void	downloadedbytes(off_t size, time_t elapsed);
diff --git a/src/sbbs3/telgate.cpp b/src/sbbs3/telgate.cpp
index 7e7fd56f5d..672c24e604 100644
--- a/src/sbbs3/telgate.cpp
+++ b/src/sbbs3/telgate.cpp
@@ -343,7 +343,8 @@ bool sbbs_t::telnet_gate(char* destaddr, uint mode, unsigned timeout, str_list_t
 	if(mode&TG_NOTERMTYPE)
 		request_telnet_opt(TELNET_DONT,TELNET_TERM_TYPE, 3000);	// Re-negotiation of terminal type
 
-	/* Text/NVT mode by default */
+	/* Text/NVT mode by default (for Ctrl-] menu support) */
+	bool remote_was_binary_tx = (telnet_remote_option[TELNET_BINARY_TX] == TELNET_WILL);
 	request_telnet_opt(TELNET_DONT,TELNET_BINARY_TX, 3000);
 	if(!(telnet_mode&TELNET_MODE_OFF) && (mode&TG_PASSTHRU))
 		telnet_mode|=TELNET_MODE_GATE;	// Pass-through telnet commands
@@ -469,6 +470,8 @@ bool sbbs_t::telnet_gate(char* destaddr, uint mode, unsigned timeout, str_list_t
 
 	/* Disable Telnet Terminal Echo */
 	request_telnet_opt(TELNET_WILL,TELNET_ECHO);
+	if(remote_was_binary_tx)
+		request_telnet_opt(TELNET_DO,TELNET_BINARY_TX);
 
 	close_socket(remote_socket);
 
diff --git a/src/sbbs3/xtrn.cpp b/src/sbbs3/xtrn.cpp
index b0e1c9182a..67db8944c6 100644
--- a/src/sbbs3/xtrn.cpp
+++ b/src/sbbs3/xtrn.cpp
@@ -925,9 +925,6 @@ int sbbs_t::external(const char* cmdline, int mode, const char* startup_dir)
 		attr(LIGHTGRAY);	// Force to "normal"
 
 		rio_abortable=rio_abortable_save;	// Restore abortable state
-
-		/* Got back to Text/NVT mode */
-		request_telnet_opt(TELNET_DONT,TELNET_BINARY_TX);
 	}
 
 //	lprintf("%s returned %d",realcmdline, retval);
@@ -2051,9 +2048,6 @@ int sbbs_t::external(const char* cmdline, int mode, const char* startup_dir)
 		attr(LIGHTGRAY);	// Force to "normal"
 
 		rio_abortable=rio_abortable_save;	// Restore abortable state
-
-		/* Got back to Text/NVT mode */
-		request_telnet_opt(TELNET_DONT,TELNET_BINARY_TX);
 	}
 
 	if(!(mode&EX_NOLOG))
-- 
GitLab