1 ///////////////////////////////////////////////////////////////////////////////
2 //Telnet Win32 : an ANSI telnet client.
3 //Copyright (C) 1998-2000 Paul Brannan
4 //Copyright (C) 1998 I.Ioannou
5 //Copyright (C) 1997 Brad Johnson
6 //
7 //This program is free software; you can redistribute it and/or
8 //modify it under the terms of the GNU General Public License
9 //as published by the Free Software Foundation; either version 2
10 //of the License, or (at your option) any later version.
11 //
12 //This program is distributed in the hope that it will be useful,
13 //but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //GNU General Public License for more details.
16 //
17 //You should have received a copy of the GNU General Public License
18 //along with this program; if not, write to the Free Software
19 //Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 //I.Ioannou
22 //roryt@hol.gr
23 //
24 ///////////////////////////////////////////////////////////////////////////
25 
26 ///////////////////////////////////////////////////////////////////////////////
27 //
28 // Module:		tncon.cpp
29 //
30 // Contents:	telnet console processing
31 //
32 // Product:		telnet
33 //
34 // Revisions: August 30, 1998 Paul Brannan <pbranna@clemson.edu>
35 //            July 29, 1998 Paul Brannan
36 //            June 15, 1998 Paul Brannan
37 //            May 16, 1998 Paul Brannan
38 //            5.April.1997 jbj@nounname.com
39 //            9.Dec.1996 jbj@nounname.com
40 //            Version 2.0
41 //
42 //            02.Apr.1995	igor.milavec@uni-lj.si
43 //					  Original code
44 //
45 ///////////////////////////////////////////////////////////////////////////////
46 
47 #include "precomp.h"
48 
49 #define KEYEVENT InputRecord[i].Event.KeyEvent
50 
51 // Paul Brannan 6/25/98
52 // #ifdef __MINGW32__
53 // #define KEYEVENT_CHAR KEYEVENT.AsciiChar
54 // #else
55 #define KEYEVENT_CHAR KEYEVENT.uChar.AsciiChar
56 // #endif
57 
58 #define KEYEVENT_PCHAR &KEYEVENT_CHAR
59 
60 // This is for local echo (Paul Brannan 5/16/98)
DoEcho(const char * p,int l,TConsole & Console,TNetwork & Network,NetParams * pParams)61 inline void DoEcho(const char *p, int l, TConsole &Console,
62 				   TNetwork &Network, NetParams *pParams) {
63 	// Pause the console (Paul Brannan 8/24/98)
64 	if(Network.get_local_echo()) {
65 		ResetEvent(pParams->hUnPause);
66 		SetEvent(pParams->hPause);
67 		while (!*pParams->bNetPaused); // Pause
68 
69 		Console.WriteCtrlString(p, l);
70 
71 		SetEvent(pParams->hUnPause); // Unpause
72 	}
73 }
74 
75 // This is for line mode (Paul Brannan 12/31/98)
76 static char buffer[1024];
77 static unsigned int bufptr = 0;
78 
79 // Line mode -- currently uses sga/echo to determine when to enter line mode
80 // (as in RFC 858), but correct behaviour is as described in RFC 1184.
81 // (Paul Brannan 12/31/98)
82 // FIX ME!!  What to do with unflushed data when we change from line mode
83 // to character mode?
DoLineModeSpecial(char keychar,TConsole & Console,TNetwork & Network,NetParams * pParams)84 inline bool DoLineModeSpecial(char keychar, TConsole &Console, TNetwork &Network,
85 					   NetParams *pParams) {
86 	if(keychar == VK_BACK) {
87 		if(bufptr) bufptr--;
88 		DoEcho("\b \b", 3, Console, Network, pParams);
89 		return true;
90 	} else if(keychar == VK_RETURN) {
91 		Network.WriteString(buffer, bufptr);
92 		Network.WriteString("\012", 1);
93 		DoEcho("\r\n", 2, Console, Network, pParams);
94 		bufptr = 0;
95 		return true;
96 	}
97 	return false;
98 }
99 
DoLineMode(const char * p,int p_len,TConsole & Console,TNetwork & Network)100 inline void DoLineMode(const char *p, int p_len, TConsole &Console,
101 					   TNetwork &Network) {
102 	if(Network.get_line_mode()) {
103 		if(bufptr < sizeof(buffer) + p_len - 1) {
104 			memcpy(buffer + bufptr, p, p_len);
105 			bufptr += p_len;
106 		} else {
107 			Console.Beep();
108 		}
109 	} else {
110 		Network.WriteString(p, p_len);
111 	}
112 }
113 
114 // Paul Brannan 5/27/98
115 // Fixed this code for use with appliation cursor keys
116 // This should probably be optimized; it's pretty ugly as it is
117 // Rewrite #1: now uses ClosestStateKey (Paul Brannan 12/9/98)
ClosestStateKey(WORD keyCode,DWORD keyState,KeyTranslator & KeyTrans)118 const char *ClosestStateKey(WORD keyCode, DWORD keyState,
119 							KeyTranslator &KeyTrans) {
120 	char const *p;
121 
122 	if((p = KeyTrans.TranslateKey(keyCode, keyState))) return p;
123 
124 	// Check numlock and scroll lock (Paul Brannan 9/23/98)
125 	if((p = KeyTrans.TranslateKey(keyCode, keyState & ~NUMLOCK_ON))) return p;
126 	if((p = KeyTrans.TranslateKey(keyCode, keyState & ~ENHANCED_KEY
127 		& ~NUMLOCK_ON))) return p;
128 	if((p = KeyTrans.TranslateKey(keyCode, keyState & ~SCROLLLOCK_ON))) return p;
129 	if((p = KeyTrans.TranslateKey(keyCode, keyState & ~ENHANCED_KEY
130 		& ~SCROLLLOCK_ON))) return p;
131 
132 	//	 John Ioannou (roryt@hol.gr)
133 	//   Athens 31/03/97 00:25am GMT+2
134 	//   fix for win95 CAPSLOCK bug
135 	//   first check if the user has keys with capslock and then we filter it
136 	if((p = KeyTrans.TranslateKey(keyCode, keyState & ~ENHANCED_KEY))) return p;
137 	if((p = KeyTrans.TranslateKey(keyCode, keyState & ~CAPSLOCK_ON))) return p;
138 	if((p = KeyTrans.TranslateKey(keyCode, keyState & ~ENHANCED_KEY
139 		& ~CAPSLOCK_ON))) return p;
140 
141 	return 0; // we couldn't find a suitable key translation
142 }
143 
FindClosestKey(WORD keyCode,DWORD keyState,KeyTranslator & KeyTrans)144 const char *FindClosestKey(WORD keyCode, DWORD keyState,
145 						   KeyTranslator &KeyTrans) {
146 	char const *p;
147 
148 	// Paul Brannan 7/20/98
149 	if(ini.get_alt_erase()) {
150 		if(keyCode == VK_BACK) {
151 			keyCode = VK_DELETE;
152 			keyState |= ENHANCED_KEY;
153 		} else if(keyCode == VK_DELETE && (keyState & ENHANCED_KEY)) {
154 			keyCode = VK_BACK;
155 			keyState &= ~ENHANCED_KEY;
156 		}
157 	}
158 
159 	DWORD ext_mode = KeyTrans.get_ext_mode();
160 	if(ext_mode) {
161 		// Not as fast as an unrolled loop, but certainly more
162 		// compact (Paul Brannan 12/9/98)
163 		for(DWORD j = ext_mode; j >= APP_KEY; j -= APP_KEY) {
164 			if((j | ext_mode) == ext_mode) {
165 				if((p = ClosestStateKey(keyCode, keyState | j,
166 					KeyTrans))) return p;
167 			}
168 		}
169 	}
170 	return ClosestStateKey(keyCode, keyState, KeyTrans);
171 }
172 
173 // Paul Brannan Feb. 22, 1999
do_op(tn_ops op,TNetwork & Network,Tnclip & Clipboard)174 int do_op(tn_ops op, TNetwork &Network, Tnclip &Clipboard) {
175 	switch(op) {
176 	case TN_ESCAPE:
177 		return TNPROMPT;
178 	case TN_SCROLLBACK:
179 		return TNSCROLLBACK;
180 	case TN_DIAL:
181 		return TNSPAWN;
182 	case TN_PASTE:
183 		if(ini.get_keyboard_paste()) Clipboard.Paste();
184 		else return 0;
185 		break;
186 	case TN_NULL:
187 		Network.WriteString("", 1);
188 		return 0;
189 	case TN_CR:
190 		Network.WriteString("\r", 2); // CR must be followed by NUL
191 		return 0;
192 	case TN_CRLF:
193 		Network.WriteString("\r\n", 2);
194 		return 0;
195 	}
196 	return 0;
197 }
198 
telProcessConsole(NetParams * pParams,KeyTranslator & KeyTrans,TConsole & Console,TNetwork & Network,TMouse & Mouse,Tnclip & Clipboard,HANDLE hThread)199 int telProcessConsole(NetParams *pParams, KeyTranslator &KeyTrans,
200 					  TConsole &Console, TNetwork &Network, TMouse &Mouse,
201 					  Tnclip &Clipboard, HANDLE hThread)
202 {
203 	KeyDefType_const keydef;
204 	const char *p;
205 	int p_len;
206 	unsigned int i;
207 	int opval;
208 	HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE);
209 
210 	SetConsoleMode(hConsole, ini.get_enable_mouse() ? ENABLE_MOUSE_INPUT : 0);
211 
212 	const DWORD nHandle = 2;
213 	HANDLE hHandle[nHandle] = {hConsole, pParams->hExit};
214 
215 	for (;;) {
216 		DWORD dwInput;
217 		switch (WaitForMultipleObjects(nHandle, hHandle, FALSE, INFINITE)) {
218         case WAIT_OBJECT_0: {
219 
220 			// Paul Brannan 7/29/98
221 			if(ini.get_input_redir()) {
222 				char InputBuffer[10];
223 
224 				// Correction from Joe Manns <joe.manns@ardenenginneers.com>
225 				// to fix race conditions (4/13/99)
226 				int bResult;
227 				bResult = ReadFile(hConsole, InputBuffer, 10, &dwInput, 0);
228 				if(bResult && dwInput == 0) return TNNOCON;
229 
230 				// no key translation for redirected input
231 				Network.WriteString(InputBuffer, dwInput);
232 				break;
233 			}
234 
235 			INPUT_RECORD InputRecord[11];
236 			if (!ReadConsoleInput(hConsole, &InputRecord[0], 10, &dwInput))
237 				return TNPROMPT;
238 
239 			for (i = 0; (unsigned)i < dwInput; i++){
240 				switch (InputRecord[i].EventType) {
241 				case KEY_EVENT:{
242 					if (KEYEVENT.bKeyDown) {
243 
244 						WORD  keyCode  = KEYEVENT.wVirtualKeyCode;
245 						DWORD keyState = KEYEVENT.dwControlKeyState;
246 
247 						// Paul Brannan 5/27/98
248 						// Moved the code that was here to FindClosestKey()
249 						keydef.szKeyDef = FindClosestKey(keyCode,
250 							keyState, KeyTrans);
251 
252 						if(keydef.szKeyDef) {
253 							if(!keydef.op->sendstr)
254 								if((opval = do_op(keydef.op->the_op, Network,
255 									Clipboard)) != 0)
256 									return opval;
257 						}
258 
259 						if(Network.get_line_mode()) {
260 							if(DoLineModeSpecial(KEYEVENT_CHAR, Console, Network, pParams))
261 								continue;
262 						}
263 
264 						p = keydef.szKeyDef;
265 						if (p == NULL) { // if we don't have a translator
266 							if(!KEYEVENT_CHAR) continue;
267 							p_len = 1;
268 							p = KEYEVENT_PCHAR;
269 						} else {
270 							p_len = strlen(p);
271 						}
272 
273 						// Local echo (Paul Brannan 5/16/98)
274 						DoEcho(p, p_len, Console, Network, pParams);
275 						// Line mode (Paul Brannan 12/31/98)
276 						DoLineMode(p, p_len, Console, Network);
277 					}
278 							   }
279 					break;
280 
281 				case MOUSE_EVENT:
282 					if(!InputRecord[i].Event.MouseEvent.dwEventFlags) {
283 						ResetEvent(pParams->hUnPause);
284 						SetEvent(pParams->hPause);
285 						while (!*pParams->bNetPaused);	// thread paused
286 						// SuspendThread(hThread);
287 
288 						// Put the mouse's X and Y coords back into the
289 						// input buffer
290 						DWORD Result;
291 						WriteConsoleInput(hConsole, &InputRecord[i], 1,
292 							&Result);
293 
294 						Mouse.doMouse();
295 
296 						SetEvent(pParams->hUnPause);
297 						// ResumeThread(hThread);
298 					}
299 					break;
300 
301 				case FOCUS_EVENT:
302 					break;
303 				case WINDOW_BUFFER_SIZE_EVENT:
304 					// FIX ME!!  This should take care of the window re-sizing bug
305 					// Unfortunately, it doesn't.
306 					Console.sync();
307 					Network.do_naws(Console.GetWidth(), Console.GetHeight());
308 					break;
309 				}
310 
311 			} // keep going until no more input
312 			break;
313 							}
314         default:
315 			return TNNOCON;
316 		}
317 	}
318 }
319 
scrollkeys()320 WORD scrollkeys() {
321 	HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE);
322 	INPUT_RECORD InputRecord;
323 	BOOL done = FALSE;
324 
325 	while (!done) {
326 		DWORD dwInput;
327 		WaitForSingleObject( hConsole, INFINITE );
328 		if (!ReadConsoleInput(hConsole, &InputRecord, 1, &dwInput)){
329 			done = TRUE;
330 			continue;
331 		}
332 		if (InputRecord.EventType == KEY_EVENT &&
333 			InputRecord.Event.KeyEvent.bKeyDown ) {
334 			// Why not just return the key code?  (Paul Brannan 12/5/98)
335 			return InputRecord.Event.KeyEvent.wVirtualKeyCode;
336 		} else if(InputRecord.EventType == MOUSE_EVENT) {
337 			if(!InputRecord.Event.MouseEvent.dwEventFlags) {
338 				// Put the mouse's X and Y coords back into the input buffer
339 				WriteConsoleInput(hConsole, &InputRecord, 1, &dwInput);
340 				return SC_MOUSE;
341 			}
342 		}
343 	}
344 	return SC_ESC;
345 }
346 
347 // FIX ME!!  This is more evidence that tncon.cpp ought to have class structure
348 // (Paul Brannan 12/10/98)
349 
350 // Bryan Montgomery 10/14/98
351 static TNetwork net;
setTNetwork(TNetwork tnet)352 void setTNetwork(TNetwork tnet) {
353 	net = tnet;
354 }
355 
356 // Thomas Briggs 8/17/98
ControlEventHandler(DWORD event)357 BOOL WINAPI ControlEventHandler(DWORD event) {
358 	switch(event) {
359 	case CTRL_BREAK_EVENT:
360 		// Bryan Montgomery 10/14/98
361 		if(ini.get_control_break_as_c()) net.WriteString("\x3",1);
362 		return TRUE;
363 	default:
364 		return FALSE;
365 	}
366 }
367