/////////////////////////////////////////////////////////////////////////////// //Telnet Win32 : an ANSI telnet client. //Copyright (C) 1998-2000 Paul Brannan //Copyright (C) 1998 I.Ioannou //Copyright (C) 1997 Brad Johnson // //This program is free software; you can redistribute it and/or //modify it under the terms of the GNU General Public License //as published by the Free Software Foundation; either version 2 //of the License, or (at your option) any later version. // //This program is distributed in the hope that it will be useful, //but WITHOUT ANY WARRANTY; without even the implied warranty of //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //GNU General Public License for more details. // //You should have received a copy of the GNU General Public License //along with this program; if not, write to the Free Software //Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // //I.Ioannou //roryt@hol.gr // /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // // Module: tncon.cpp // // Contents: telnet console processing // // Product: telnet // // Revisions: August 30, 1998 Paul Brannan // July 29, 1998 Paul Brannan // June 15, 1998 Paul Brannan // May 16, 1998 Paul Brannan // 5.April.1997 jbj@nounname.com // 9.Dec.1996 jbj@nounname.com // Version 2.0 // // 02.Apr.1995 igor.milavec@uni-lj.si // Original code // /////////////////////////////////////////////////////////////////////////////// #include "precomp.h" #define KEYEVENT InputRecord[i].Event.KeyEvent // Paul Brannan 6/25/98 // #ifdef __MINGW32__ // #define KEYEVENT_CHAR KEYEVENT.AsciiChar // #else #define KEYEVENT_CHAR KEYEVENT.uChar.AsciiChar // #endif #define KEYEVENT_PCHAR &KEYEVENT_CHAR // This is for local echo (Paul Brannan 5/16/98) inline void DoEcho(const char *p, int l, TConsole &Console, TNetwork &Network, NetParams *pParams) { // Pause the console (Paul Brannan 8/24/98) if(Network.get_local_echo()) { ResetEvent(pParams->hUnPause); SetEvent(pParams->hPause); while (!*pParams->bNetPaused); // Pause Console.WriteCtrlString(p, l); SetEvent(pParams->hUnPause); // Unpause } } // This is for line mode (Paul Brannan 12/31/98) static char buffer[1024]; static unsigned int bufptr = 0; // Line mode -- currently uses sga/echo to determine when to enter line mode // (as in RFC 858), but correct behaviour is as described in RFC 1184. // (Paul Brannan 12/31/98) // FIX ME!! What to do with unflushed data when we change from line mode // to character mode? inline bool DoLineModeSpecial(char keychar, TConsole &Console, TNetwork &Network, NetParams *pParams) { if(keychar == VK_BACK) { if(bufptr) bufptr--; DoEcho("\b \b", 3, Console, Network, pParams); return true; } else if(keychar == VK_RETURN) { Network.WriteString(buffer, bufptr); Network.WriteString("\012", 1); DoEcho("\r\n", 2, Console, Network, pParams); bufptr = 0; return true; } return false; } inline void DoLineMode(const char *p, int p_len, TConsole &Console, TNetwork &Network) { if(Network.get_line_mode()) { if(bufptr < sizeof(buffer) + p_len - 1) { memcpy(buffer + bufptr, p, p_len); bufptr += p_len; } else { Console.Beep(); } } else { Network.WriteString(p, p_len); } } // Paul Brannan 5/27/98 // Fixed this code for use with appliation cursor keys // This should probably be optimized; it's pretty ugly as it is // Rewrite #1: now uses ClosestStateKey (Paul Brannan 12/9/98) const char *ClosestStateKey(WORD keyCode, DWORD keyState, KeyTranslator &KeyTrans) { char const *p; if((p = KeyTrans.TranslateKey(keyCode, keyState))) return p; // Check numlock and scroll lock (Paul Brannan 9/23/98) if((p = KeyTrans.TranslateKey(keyCode, keyState & ~NUMLOCK_ON))) return p; if((p = KeyTrans.TranslateKey(keyCode, keyState & ~ENHANCED_KEY & ~NUMLOCK_ON))) return p; if((p = KeyTrans.TranslateKey(keyCode, keyState & ~SCROLLLOCK_ON))) return p; if((p = KeyTrans.TranslateKey(keyCode, keyState & ~ENHANCED_KEY & ~SCROLLLOCK_ON))) return p; // John Ioannou (roryt@hol.gr) // Athens 31/03/97 00:25am GMT+2 // fix for win95 CAPSLOCK bug // first check if the user has keys with capslock and then we filter it if((p = KeyTrans.TranslateKey(keyCode, keyState & ~ENHANCED_KEY))) return p; if((p = KeyTrans.TranslateKey(keyCode, keyState & ~CAPSLOCK_ON))) return p; if((p = KeyTrans.TranslateKey(keyCode, keyState & ~ENHANCED_KEY & ~CAPSLOCK_ON))) return p; return 0; // we couldn't find a suitable key translation } const char *FindClosestKey(WORD keyCode, DWORD keyState, KeyTranslator &KeyTrans) { char const *p; // Paul Brannan 7/20/98 if(ini.get_alt_erase()) { if(keyCode == VK_BACK) { keyCode = VK_DELETE; keyState |= ENHANCED_KEY; } else if(keyCode == VK_DELETE && (keyState & ENHANCED_KEY)) { keyCode = VK_BACK; keyState &= ~ENHANCED_KEY; } } DWORD ext_mode = KeyTrans.get_ext_mode(); if(ext_mode) { // Not as fast as an unrolled loop, but certainly more // compact (Paul Brannan 12/9/98) for(DWORD j = ext_mode; j >= APP_KEY; j -= APP_KEY) { if((j | ext_mode) == ext_mode) { if((p = ClosestStateKey(keyCode, keyState | j, KeyTrans))) return p; } } } return ClosestStateKey(keyCode, keyState, KeyTrans); } // Paul Brannan Feb. 22, 1999 int do_op(tn_ops op, TNetwork &Network, Tnclip &Clipboard) { switch(op) { case TN_ESCAPE: return TNPROMPT; case TN_SCROLLBACK: return TNSCROLLBACK; case TN_DIAL: return TNSPAWN; case TN_PASTE: if(ini.get_keyboard_paste()) Clipboard.Paste(); else return 0; break; case TN_NULL: Network.WriteString("", 1); return 0; case TN_CR: Network.WriteString("\r", 2); // CR must be followed by NUL return 0; case TN_CRLF: Network.WriteString("\r\n", 2); return 0; } return 0; } int telProcessConsole(NetParams *pParams, KeyTranslator &KeyTrans, TConsole &Console, TNetwork &Network, TMouse &Mouse, Tnclip &Clipboard, HANDLE hThread) { KeyDefType_const keydef; const char *p; int p_len; unsigned int i; int opval; HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE); SetConsoleMode(hConsole, ini.get_enable_mouse() ? ENABLE_MOUSE_INPUT : 0); const DWORD nHandle = 2; HANDLE hHandle[nHandle] = {hConsole, pParams->hExit}; for (;;) { DWORD dwInput; switch (WaitForMultipleObjects(nHandle, hHandle, FALSE, INFINITE)) { case WAIT_OBJECT_0: { // Paul Brannan 7/29/98 if(ini.get_input_redir()) { char InputBuffer[10]; // Correction from Joe Manns // to fix race conditions (4/13/99) int bResult; bResult = ReadFile(hConsole, InputBuffer, 10, &dwInput, 0); if(bResult && dwInput == 0) return TNNOCON; // no key translation for redirected input Network.WriteString(InputBuffer, dwInput); break; } INPUT_RECORD InputRecord[11]; if (!ReadConsoleInput(hConsole, &InputRecord[0], 10, &dwInput)) return TNPROMPT; for (i = 0; (unsigned)i < dwInput; i++){ switch (InputRecord[i].EventType) { case KEY_EVENT:{ if (KEYEVENT.bKeyDown) { WORD keyCode = KEYEVENT.wVirtualKeyCode; DWORD keyState = KEYEVENT.dwControlKeyState; // Paul Brannan 5/27/98 // Moved the code that was here to FindClosestKey() keydef.szKeyDef = FindClosestKey(keyCode, keyState, KeyTrans); if(keydef.szKeyDef) { if(!keydef.op->sendstr) if((opval = do_op(keydef.op->the_op, Network, Clipboard)) != 0) return opval; } if(Network.get_line_mode()) { if(DoLineModeSpecial(KEYEVENT_CHAR, Console, Network, pParams)) continue; } p = keydef.szKeyDef; if (p == NULL) { // if we don't have a translator if(!KEYEVENT_CHAR) continue; p_len = 1; p = KEYEVENT_PCHAR; } else { p_len = strlen(p); } // Local echo (Paul Brannan 5/16/98) DoEcho(p, p_len, Console, Network, pParams); // Line mode (Paul Brannan 12/31/98) DoLineMode(p, p_len, Console, Network); } } break; case MOUSE_EVENT: if(!InputRecord[i].Event.MouseEvent.dwEventFlags) { ResetEvent(pParams->hUnPause); SetEvent(pParams->hPause); while (!*pParams->bNetPaused); // thread paused // SuspendThread(hThread); // Put the mouse's X and Y coords back into the // input buffer DWORD Result; WriteConsoleInput(hConsole, &InputRecord[i], 1, &Result); Mouse.doMouse(); SetEvent(pParams->hUnPause); // ResumeThread(hThread); } break; case FOCUS_EVENT: break; case WINDOW_BUFFER_SIZE_EVENT: // FIX ME!! This should take care of the window re-sizing bug // Unfortunately, it doesn't. Console.sync(); Network.do_naws(Console.GetWidth(), Console.GetHeight()); break; } } // keep going until no more input break; } default: return TNNOCON; } } } WORD scrollkeys() { HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE); INPUT_RECORD InputRecord; BOOL done = FALSE; while (!done) { DWORD dwInput; WaitForSingleObject( hConsole, INFINITE ); if (!ReadConsoleInput(hConsole, &InputRecord, 1, &dwInput)){ done = TRUE; continue; } if (InputRecord.EventType == KEY_EVENT && InputRecord.Event.KeyEvent.bKeyDown ) { // Why not just return the key code? (Paul Brannan 12/5/98) return InputRecord.Event.KeyEvent.wVirtualKeyCode; } else if(InputRecord.EventType == MOUSE_EVENT) { if(!InputRecord.Event.MouseEvent.dwEventFlags) { // Put the mouse's X and Y coords back into the input buffer WriteConsoleInput(hConsole, &InputRecord, 1, &dwInput); return SC_MOUSE; } } } return SC_ESC; } // FIX ME!! This is more evidence that tncon.cpp ought to have class structure // (Paul Brannan 12/10/98) // Bryan Montgomery 10/14/98 static TNetwork net; void setTNetwork(TNetwork tnet) { net = tnet; } // Thomas Briggs 8/17/98 BOOL WINAPI ControlEventHandler(DWORD event) { switch(event) { case CTRL_BREAK_EVENT: // Bryan Montgomery 10/14/98 if(ini.get_control_break_as_c()) net.WriteString("\x3",1); return TRUE; default: return FALSE; } }