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