You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2048 lines
62 KiB
2048 lines
62 KiB
// scraper.cpp : This file contains the
|
|
// Created: Dec '97
|
|
// History:
|
|
// Copyright (C) 1997 Microsoft Corporation
|
|
// All rights reserved.
|
|
// Microsoft Confidential
|
|
|
|
#include <windows.h>
|
|
#include <wincon.h>
|
|
#include <msgFile.h>
|
|
#include <telnetd.h>
|
|
#include <debug.h>
|
|
#include <regutil.h>
|
|
#include <TermCap.h>
|
|
#include <Scraper.h>
|
|
#include <Session.h>
|
|
#include <TlntUtils.h>
|
|
#include <Ipc.h>
|
|
#include <vtnt.h>
|
|
|
|
#define ONE_KB 1024
|
|
|
|
#pragma warning( disable: 4706 )
|
|
#pragma warning( disable: 4127 )
|
|
|
|
using namespace _Utils;
|
|
using CDebugLevel::TRACE_DEBUGGING;
|
|
using CDebugLevel::TRACE_HANDLE;
|
|
using CDebugLevel::TRACE_SOCKET;
|
|
|
|
WORD wRows;
|
|
WORD wCols;
|
|
COORD coExpectedCursor = { ~0, ~0 };
|
|
|
|
extern HANDLE g_hSyncCloseHandle;
|
|
|
|
CTermCap* pTermCap = 0;
|
|
LPSTR lpszCMStr1 = 0;
|
|
LPSTR lpszCMStr2 = 0;
|
|
|
|
CHAR szSGRStr[25]; //the string is of the form <ESC>[ Ps m
|
|
bool fNegativeImage = false, fBold = false;
|
|
WORD wExistingAttributes = 0;
|
|
WORD wDefaultAttributes = 0;
|
|
|
|
static PUCHAR g_pucSendBuffer = 0;
|
|
static DWORD g_dwSendBufferSize = 0;
|
|
static PUCHAR g_pucSendInsertPoint = 0;
|
|
|
|
void
|
|
CScraper::DeleteCMStrings()
|
|
{
|
|
delete[] lpszCMStr1;
|
|
delete[] lpszCMStr2;
|
|
}
|
|
void
|
|
CScraper::LoadStrings()
|
|
{
|
|
//ce --> String to clear from the cursor to the end of the line
|
|
lpszCMStr1 = pTermCap->GetString( "ce" );
|
|
|
|
//cm --> String to position the cursor at row r, column c
|
|
lpszCMStr2 = pTermCap->GetString( "cm" );
|
|
}
|
|
|
|
CScraper::CScraper()
|
|
{
|
|
pLastSeen = NULL;
|
|
pCurrent = NULL;
|
|
m_dwHowLongIdle = 0;
|
|
m_bCheckForScrolling = 1;
|
|
m_dwTerm = 0;
|
|
m_hConBufIn = NULL;
|
|
m_hConBufOut = NULL;
|
|
}
|
|
|
|
CScraper::~CScraper()
|
|
{
|
|
// Cleanup before exit
|
|
TELNET_CLOSE_HANDLE( m_hConBufIn );
|
|
delete [] pCurrent;
|
|
delete [] pLastSeen;
|
|
delete [] g_pucSendBuffer;
|
|
DeleteCMStrings();
|
|
delete pTermCap;
|
|
}
|
|
|
|
bool
|
|
CScraper::InitializeNonVtntTerms()
|
|
{
|
|
if( !pTermCap->LoadEntry( m_pSession->m_pszTermType ) )
|
|
{
|
|
LPWSTR szTermType = NULL;
|
|
_TRACE( TRACE_DEBUGGING, "TermCap LoadEntry() failed : %s",
|
|
m_pSession->m_pszTermType );
|
|
ConvertSChartoWChar( m_pSession->m_pszTermType, &szTermType );
|
|
LogEvent( EVENTLOG_ERROR_TYPE, MSG_NOENTRY_IN_TERMCAP, szTermType );
|
|
|
|
if( !pTermCap->LoadEntry( VT100 ) )
|
|
{
|
|
_TRACE( TRACE_DEBUGGING, "TermCap LoadEntry() failed" );
|
|
LogEvent( EVENTLOG_ERROR_TYPE, MSG_ERROR_READING_TERMCAP, _T("") );
|
|
return( FALSE );
|
|
}
|
|
|
|
_TRACE( TRACE_DEBUGGING, "TermType is vt100");
|
|
}
|
|
|
|
LoadStrings();
|
|
|
|
if( ( lpszCMStr1 == NULL ) || ( lpszCMStr2 == NULL ) )
|
|
{
|
|
DeleteCMStrings();
|
|
if( !pTermCap->LoadEntry( VT100 ) )
|
|
{
|
|
_TRACE( TRACE_DEBUGGING, "TermCap LoadEntry() failed" );
|
|
LogEvent( EVENTLOG_ERROR_TYPE, MSG_ERROR_READING_TERMCAP, _T("") );
|
|
}
|
|
else
|
|
{
|
|
_TRACE( TRACE_DEBUGGING, "TermType is vt100");
|
|
LogEvent( EVENTLOG_ERROR_TYPE, MSG_NOENTRY_IN_TERMCAP, _T("") );
|
|
LoadStrings();
|
|
}
|
|
}
|
|
|
|
pTermCap->ProcessString( &lpszCMStr1 );
|
|
pTermCap->ProcessString( &lpszCMStr2 );
|
|
|
|
if( m_pSession->CSession::m_bIsStreamMode )
|
|
{
|
|
//ask for crlf for an enter when in stream mode
|
|
|
|
//Set new line mode for the virtual terminal
|
|
CHAR szLNMString[ SMALL_ARRAY + 1];
|
|
|
|
_snprintf( szLNMString, SMALL_ARRAY, "%c[%dh", VT_ESC, LNM );
|
|
szLNMString[SMALL_ARRAY] = '\0';
|
|
|
|
SendString( szLNMString );
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
bool AllocateSendBuffer( )
|
|
{
|
|
g_dwSendBufferSize = DEFAULT_SEND_BUFFER_SIZE;
|
|
g_pucSendBuffer = new UCHAR[g_dwSendBufferSize];
|
|
g_pucSendInsertPoint = g_pucSendBuffer;
|
|
if( !g_pucSendBuffer )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
SfuZeroMemory((LPVOID)g_pucSendBuffer, g_dwSendBufferSize);
|
|
return( TRUE );
|
|
}
|
|
|
|
bool
|
|
CScraper::InitTerm()
|
|
{
|
|
//The following is just to avoid recurring string comparisions
|
|
m_dwTerm = TERMVT100;
|
|
if( _stricmp( m_pSession->m_pszTermType, VTNT ) == 0 )
|
|
{
|
|
m_dwTerm = TERMVTNT;
|
|
}
|
|
else if ( _stricmp( m_pSession->m_pszTermType, VT52 ) == 0 )
|
|
{
|
|
m_dwTerm = TERMVT52;
|
|
}
|
|
else if ( _stricmp( m_pSession->m_pszTermType, VT100 ) == 0 )
|
|
{
|
|
m_dwTerm = TERMVT100;
|
|
}
|
|
else if ( _stricmp( m_pSession->m_pszTermType, VT80 ) == 0 )
|
|
{
|
|
m_dwTerm = TERMVT80;
|
|
}
|
|
else if ( _stricmp( m_pSession->m_pszTermType, ANSI ) == 0 )
|
|
{
|
|
m_dwTerm = TERMANSI;
|
|
}
|
|
|
|
if( !( m_dwTerm & TERMVTNT ) )
|
|
{
|
|
if( !InitializeNonVtntTerms() )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
SfuZeroMemory( &LastCSBI, sizeof( LastCSBI ) );
|
|
|
|
if( g_pucSendBuffer == NULL )
|
|
{
|
|
if( !AllocateSendBuffer() )
|
|
{
|
|
return ( FALSE );
|
|
}
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
bool
|
|
CScraper::SetWindowInfo()
|
|
{
|
|
//No Security Attrs needed.
|
|
_chVERIFY2( m_hConBufOut = CreateFile( TEXT("CONOUT$"), GENERIC_READ |
|
|
GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL, 0 ) );
|
|
|
|
if( INVALID_HANDLE_VALUE == m_hConBufOut)
|
|
{
|
|
//Could be an application that does not share its screen buffer
|
|
return ( TRUE );
|
|
}
|
|
|
|
SMALL_RECT sr;
|
|
|
|
sr.Top = 0;
|
|
sr.Bottom = ( WORD )( m_pSession->CSession::m_wRows - 1 );
|
|
sr.Right = ( WORD ) ( m_pSession->CSession::m_wCols - 1 );
|
|
sr.Left = 0;
|
|
|
|
COORD coordSize;
|
|
coordSize.X = m_pSession->CSession::m_wCols;
|
|
coordSize.Y = m_pSession->CSession::m_wRows;
|
|
|
|
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
|
_chVERIFY2( GetConsoleScreenBufferInfo( m_pSession->CScraper::m_hConBufOut,
|
|
&csbi) );
|
|
|
|
// Logic: If the Old Window Size is less than the new Size then we set
|
|
// the Screen Buffer Size first and then set the Window Size.
|
|
// If the Old Window Size is Greater than the new Size then we set the
|
|
// window Size first and then the Screen Buffer.
|
|
|
|
// The above is because the Buffer Size always has to be greater than or
|
|
// equal to the Window Size.
|
|
if ( (csbi.dwSize.X < coordSize.X) || (csbi.dwSize.Y < coordSize.Y) )
|
|
{
|
|
COORD coordTmpSize = { 0, 0 };
|
|
|
|
coordTmpSize .X = ( csbi.dwSize.X < coordSize.X ) ? coordSize.X : csbi.dwSize.X;
|
|
coordTmpSize .Y = ( csbi.dwSize.Y < coordSize.Y ) ? coordSize.Y : csbi.dwSize.Y;
|
|
|
|
_chVERIFY2( SetConsoleScreenBufferSize
|
|
( m_pSession->CScraper::m_hConBufOut, coordTmpSize ) );
|
|
_chVERIFY2( SetConsoleWindowInfo
|
|
( m_pSession->CScraper::m_hConBufOut, TRUE, &sr ) );
|
|
_chVERIFY2( SetConsoleScreenBufferSize
|
|
( m_pSession->CScraper::m_hConBufOut, coordSize ) );
|
|
}
|
|
else
|
|
{
|
|
_chVERIFY2( SetConsoleWindowInfo( m_pSession->CScraper::m_hConBufOut,
|
|
TRUE, &sr ) );
|
|
_chVERIFY2( SetConsoleScreenBufferSize( m_pSession->CScraper::
|
|
m_hConBufOut, coordSize ) );
|
|
}
|
|
TELNET_CLOSE_HANDLE( m_hConBufOut );
|
|
return( TRUE );
|
|
}
|
|
|
|
bool
|
|
CScraper::SetCmdInfo()
|
|
{
|
|
if( !SetWindowInfo() )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
wRows = m_pSession->m_wRows;
|
|
wCols = m_pSession->m_wCols;
|
|
|
|
if( pLastSeen )
|
|
{
|
|
delete[] pLastSeen;
|
|
}
|
|
if( pCurrent )
|
|
{
|
|
delete[] pCurrent;
|
|
}
|
|
|
|
pLastSeen = ( PCHAR_INFO ) new char[wRows * wCols * sizeof( CHAR_INFO )];
|
|
pCurrent = ( PCHAR_INFO ) new char[wRows * wCols * sizeof( CHAR_INFO )];
|
|
|
|
if( !pLastSeen || !pCurrent )
|
|
{
|
|
return ( FALSE );
|
|
}
|
|
|
|
SfuZeroMemory( pLastSeen, wRows * wCols * sizeof( CHAR_INFO ) );
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
void
|
|
CScraper::Init( CSession *pSession )
|
|
{
|
|
_chASSERT( pSession );
|
|
m_pSession = pSession;
|
|
m_dwCurrentCodePage = GetACP();
|
|
}
|
|
|
|
bool
|
|
CScraper::InitSession()
|
|
{
|
|
wRows = m_pSession->m_wRows;
|
|
wCols = m_pSession->m_wCols;
|
|
|
|
CONVERT_TO_MILLI_SECS( m_pSession->CSession::m_dwIdleSessionTimeOut );
|
|
|
|
m_dwPollInterval = MIN_POLL_INTERVAL;
|
|
GetRegistryValues( );
|
|
|
|
//Load the term cap entry
|
|
pTermCap = _Utils::CTermCap::Instance();
|
|
if( !pTermCap )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
pLastSeen = NULL;
|
|
pCurrent = NULL;
|
|
|
|
m_dwInputSequneceState = IP_INIT;
|
|
m_dwDigitInTheSeq = 0;
|
|
|
|
if( !InitTerm() )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if( !SetCmdInfo() )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// If Japanese NT use VT80 scraping loop
|
|
//
|
|
return( TRUE );
|
|
}
|
|
|
|
void
|
|
CScraper::Shutdown()
|
|
{
|
|
}
|
|
|
|
bool
|
|
CScraper::OnWaitTimeOut()
|
|
{
|
|
bool bRetVal = TRUE;
|
|
bool fDifferenceFound = false;
|
|
|
|
if( m_pSession->CSession::m_bIsStreamMode )
|
|
{
|
|
//No scraping.
|
|
if( g_pucSendInsertPoint != g_pucSendBuffer )
|
|
{
|
|
fDifferenceFound = true;
|
|
CScraper::Transmit();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Time to check the screen
|
|
if( ( m_dwTerm & TERMVTNT ) != 0 )
|
|
{
|
|
bRetVal = CompareAndUpdateVTNT( wRows, wCols, pCurrent, pLastSeen,
|
|
&CSBI, &LastCSBI, &fDifferenceFound );
|
|
}
|
|
else
|
|
{
|
|
bRetVal = COMPARE_AND_UPDATE ( wRows, wCols, pCurrent,
|
|
pLastSeen, &CSBI, &LastCSBI, &fDifferenceFound );
|
|
}
|
|
}
|
|
|
|
if( fDifferenceFound )
|
|
{
|
|
m_dwPollInterval = MIN_POLL_INTERVAL;
|
|
}
|
|
else
|
|
{
|
|
m_dwPollInterval = MAX_POLL_INTERVAL;
|
|
}
|
|
|
|
return( bRetVal );
|
|
}
|
|
|
|
bool
|
|
CScraper::IsSessionTimedOut()
|
|
{
|
|
if( m_dwPollInterval == MAX_POLL_INTERVAL )
|
|
{
|
|
m_dwHowLongIdle += MAX_POLL_INTERVAL;
|
|
m_pSession->CIoHandler::UpdateIdleTime( UPDATE_IDLE_TIME );
|
|
if( m_dwHowLongIdle > m_pSession->CSession::m_dwIdleSessionTimeOut &&
|
|
!ISSESSION_TIMEOUT_DISABLED( m_pSession->CSession::m_dwIdleSessionTimeOut) )
|
|
{
|
|
LPWSTR szMsg = NULL;
|
|
szMsg = new WCHAR[ 2 * wcslen( _T( "\r\n" ) ) + wcslen( _T( TIMEOUT_STR ) ) + wcslen( _T( TERMINATE ) ) + 1];
|
|
if( szMsg )
|
|
{
|
|
wcscpy( szMsg, _T( "\r\n" ) );
|
|
wcscat( szMsg, _T( TERMINATE ) );
|
|
wcscat( szMsg, _T( TIMEOUT_STR ) );
|
|
wcscat( szMsg, _T( "\r\n" ) );
|
|
m_pSession->CIoHandler::SendMessageToClient( szMsg, NO_HEADER );
|
|
delete[] szMsg;
|
|
}
|
|
return( true ); //This will take us out of WaitForIo
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( m_dwHowLongIdle != 0 )
|
|
{
|
|
m_pSession->CIoHandler::UpdateIdleTime( RESET_IDLE_TIME );
|
|
}
|
|
|
|
m_dwHowLongIdle = 0;
|
|
}
|
|
return( false );
|
|
}
|
|
|
|
void CScraper::EchoVtntCharToClient( INPUT_RECORD *pIR )
|
|
{
|
|
_chASSERT( pIR );
|
|
CHAR rgchInput;
|
|
|
|
if( !pIR )
|
|
{
|
|
return;
|
|
}
|
|
|
|
DWORD dwSize = 0;
|
|
PUCHAR pResponse = NULL;
|
|
rgchInput = pIR->Event.KeyEvent.uChar.AsciiChar;
|
|
m_pSession->CRFCProtocol::StrToVTNTResponse( &( rgchInput ), 1, ( VOID **)&pResponse, &dwSize );
|
|
SendBytes( pResponse, dwSize );
|
|
delete[] pResponse;
|
|
}
|
|
|
|
inline void
|
|
CScraper::EchoCharBackToClient( UCHAR ucChar )
|
|
{
|
|
SendChar( ucChar );
|
|
}
|
|
|
|
bool
|
|
CScraper::EmulateAndWriteToCmdConsoleInput()
|
|
{
|
|
CHAR cPrevChar;
|
|
DWORD dwCount;
|
|
SHORT vk;
|
|
BYTE vkcode;
|
|
DWORD dwShiftcode;
|
|
WORD wScanCode;
|
|
DWORD i;
|
|
BOOL dwStatus = 0;
|
|
DWORD dwBytesTransferred = 0;
|
|
|
|
cPrevChar = '\0';
|
|
|
|
dwBytesTransferred = m_pSession->CIoHandler::m_dwReadFromSocketIoLength;
|
|
|
|
m_dwPollInterval = MIN_POLL_INTERVAL;
|
|
|
|
if( ( m_dwTerm & TERMVTNT ) != 0 )
|
|
{
|
|
DWORD dwBytesRemaining = 0;
|
|
DWORD dwBytesProcessed = 0;
|
|
INPUT_RECORD* pIR = (INPUT_RECORD*) m_pSession->CIoHandler::
|
|
m_ReadFromSocketBuffer;
|
|
dwBytesTransferred += (DWORD)(m_pSession->CIoHandler::m_pReadFromSocketBufferCursor - m_pSession->CIoHandler::m_ReadFromSocketBuffer);
|
|
|
|
if( m_pSession->CSession::m_bIsStreamMode &&
|
|
pIR->EventType == KEY_EVENT &&
|
|
pIR->Event.KeyEvent.bKeyDown &&
|
|
pIR->Event.KeyEvent.uChar.AsciiChar
|
|
)
|
|
{
|
|
EchoVtntCharToClient( pIR );
|
|
}
|
|
|
|
if( pIR->Event.KeyEvent.uChar.AsciiChar != '\r' )
|
|
{
|
|
m_bCheckForScrolling = 0 ;
|
|
}
|
|
|
|
if( (pIR->EventType == KEY_EVENT) &&
|
|
pIR->Event.KeyEvent.uChar.AsciiChar == CTRLC )
|
|
{
|
|
DWORD dwMode = 0;
|
|
_chVERIFY2( GetConsoleMode( m_hConBufIn, &dwMode ) );
|
|
if( ( ( dwMode & ENABLE_PROCESSED_INPUT ) && pIR->Event.KeyEvent.bKeyDown )
|
|
|| pIR->Event.KeyEvent.wVirtualKeyCode == VKEY_CTRL_BREAK )
|
|
{
|
|
_chVERIFY2( GenerateConsoleCtrlEvent( CTRL_C_EVENT, 0));
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
if( dwBytesRemaining = (dwBytesTransferred % sizeof(INPUT_RECORD)) )
|
|
{
|
|
dwBytesProcessed = dwBytesTransferred - dwBytesRemaining;
|
|
}
|
|
else
|
|
{
|
|
dwBytesProcessed=dwBytesTransferred;
|
|
}
|
|
|
|
_chVERIFY2( dwStatus = WriteConsoleInput( m_hConBufIn,
|
|
pIR, dwBytesProcessed/sizeof(INPUT_RECORD), &dwCount));
|
|
if( !dwStatus )
|
|
{
|
|
LogFormattedGetLastError( EVENTLOG_ERROR_TYPE,
|
|
MSG_ERR_WRITE_CONSOLE, GetLastError() );
|
|
return ( FALSE );
|
|
}
|
|
|
|
if( dwBytesRemaining > 0 )
|
|
{
|
|
memmove( (void *)m_pSession->CIoHandler::m_ReadFromSocketBuffer, (void *)(m_pSession->CIoHandler::m_ReadFromSocketBuffer+dwBytesProcessed), dwBytesRemaining);
|
|
}
|
|
m_pSession->CIoHandler::m_pReadFromSocketBufferCursor = (PUCHAR)(m_pSession->CIoHandler::m_ReadFromSocketBuffer + dwBytesRemaining);
|
|
}
|
|
else
|
|
{
|
|
m_bCheckForScrolling = 1;
|
|
if( dwBytesTransferred == 1 )
|
|
{
|
|
if( m_pSession->CIoHandler::m_ReadFromSocketBuffer[0] != '\r' )
|
|
{
|
|
m_bCheckForScrolling = 0;
|
|
}
|
|
}
|
|
|
|
for( i=0; i < dwBytesTransferred; i++ )
|
|
{
|
|
if( m_pSession->CSession::m_bIsStreamMode )
|
|
{
|
|
EchoCharBackToClient( m_pSession->CIoHandler::
|
|
m_ReadFromSocketBuffer[i] );
|
|
}
|
|
|
|
if( ProcessEnhancedKeys( m_pSession->CIoHandler::
|
|
m_ReadFromSocketBuffer[i], &cPrevChar,
|
|
dwBytesTransferred == 1) )
|
|
{
|
|
m_bCheckForScrolling = 0;
|
|
continue;
|
|
}
|
|
|
|
if( cPrevChar != '\r' || m_pSession->CIoHandler::
|
|
m_ReadFromSocketBuffer[i] != '\n' )
|
|
{
|
|
if( m_pSession->CIoHandler::m_ReadFromSocketBuffer[i] == CTRLC )
|
|
{
|
|
//The follwing is the observed behaviour of CTRL C
|
|
//When ENABLE_PROCESSED_INPUT mode is not enabled, pass CTRL C as
|
|
//input to the console input buffer.
|
|
//When ENABLE_PROCESSED_INPUT mode is enabled, generate CTTRL C signal
|
|
//and also unblock any ReadConsoleInput. This behaviour is what is observed
|
|
// and not from any documentation.
|
|
DWORD dwMode = 0;
|
|
_chVERIFY2( GetConsoleMode( m_hConBufIn, &dwMode ) );
|
|
if( dwMode & ENABLE_PROCESSED_INPUT )
|
|
{
|
|
_chVERIFY2( GenerateConsoleCtrlEvent( CTRL_C_EVENT, 0));
|
|
continue;
|
|
}
|
|
}
|
|
vk = VkKeyScan( m_pSession->CIoHandler::
|
|
m_ReadFromSocketBuffer[i] );
|
|
if( vk != 0xffff )
|
|
{
|
|
vkcode = LOBYTE( vk );
|
|
wScanCode = ( WORD )MapVirtualKey( vkcode, 0 );
|
|
dwShiftcode = 0;
|
|
if( HIBYTE( vk ) & 1 )
|
|
dwShiftcode |= SHIFT_PRESSED;
|
|
if( HIBYTE( vk ) & 2 )
|
|
dwShiftcode |= LEFT_CTRL_PRESSED;
|
|
if( HIBYTE( vk ) & 4 )
|
|
dwShiftcode |= LEFT_ALT_PRESSED;
|
|
|
|
dwStatus = WriteAKeyToCMD( vkcode, wScanCode,
|
|
m_pSession->CIoHandler::m_ReadFromSocketBuffer[i],
|
|
dwShiftcode );
|
|
|
|
if( !dwStatus )
|
|
{
|
|
LogFormattedGetLastError( EVENTLOG_ERROR_TYPE,
|
|
MSG_ERR_WRITE_CONSOLE, GetLastError() );
|
|
return ( FALSE );
|
|
}
|
|
}
|
|
}
|
|
cPrevChar = m_pSession->CIoHandler::m_ReadFromSocketBuffer[i];
|
|
}
|
|
}
|
|
return( TRUE );
|
|
}
|
|
|
|
DWORD
|
|
CScraper::WriteAKeyToCMD( WORD wVKCode, WORD wVSCode, CHAR cChar, DWORD dwCKState )
|
|
{
|
|
DWORD dwStatus = 0;
|
|
DWORD dwCount = 0;
|
|
INPUT_RECORD input;
|
|
|
|
SfuZeroMemory( &input, sizeof( INPUT_RECORD ) );
|
|
|
|
input.EventType = KEY_EVENT;
|
|
input.Event.KeyEvent.bKeyDown = TRUE;
|
|
input.Event.KeyEvent.wRepeatCount = 1;
|
|
|
|
input.Event.KeyEvent.wVirtualKeyCode = wVKCode;
|
|
input.Event.KeyEvent.wVirtualScanCode = wVSCode;
|
|
input.Event.KeyEvent.uChar.AsciiChar = cChar;
|
|
input.Event.KeyEvent.dwControlKeyState = dwCKState;
|
|
_chVERIFY2 ( dwStatus = WriteConsoleInputA( m_hConBufIn, &input, 1, &dwCount ) );
|
|
|
|
if( !IsDBCSLeadByte(cChar) )
|
|
{
|
|
input.Event.KeyEvent.bKeyDown = FALSE;
|
|
_chVERIFY2 ( dwStatus = WriteConsoleInputA( m_hConBufIn, &input, 1, &dwCount ) );
|
|
}
|
|
|
|
return dwStatus;
|
|
}
|
|
|
|
|
|
bool
|
|
CScraper::ProcessEnhancedKeys( unsigned char cCurrentChar, char* pchPrevChar, bool fSingleByte )
|
|
{
|
|
bool bRetVal = true;
|
|
|
|
*pchPrevChar = cCurrentChar;
|
|
|
|
switch( m_dwInputSequneceState )
|
|
{
|
|
case IP_INIT:
|
|
if( cCurrentChar == m_pSession->m_dwAltKeyMapping )
|
|
{
|
|
WriteAKeyToCMD( VK_MENU, VS_MENU, cCurrentChar, ENHANCED_KEY );
|
|
}
|
|
else if( cCurrentChar == ASCII_DELETE ) //vt100 delete key
|
|
{
|
|
WriteAKeyToCMD( VK_DELETE, VS_DELETE, 0, ENHANCED_KEY );
|
|
}
|
|
else if( cCurrentChar == ESC )
|
|
{
|
|
if( fSingleByte )
|
|
{
|
|
WriteAKeyToCMD( VK_ESCAPE, VS_ESCAPE, ESC, ENHANCED_KEY );
|
|
}
|
|
else
|
|
{
|
|
m_dwInputSequneceState = IP_ESC_RCVD;
|
|
}
|
|
}else
|
|
{
|
|
//Not any special char
|
|
bRetVal = false;
|
|
}
|
|
break;
|
|
|
|
case IP_ESC_RCVD:
|
|
if( cCurrentChar == '[' )
|
|
{
|
|
m_dwInputSequneceState = IP_ESC_BRACKET_RCVD;
|
|
}
|
|
else if( cCurrentChar == 'O' )
|
|
{
|
|
m_dwInputSequneceState = IP_ESC_O_RCVD;
|
|
}
|
|
else
|
|
{
|
|
//Write already received escape as it is and return false
|
|
WriteAKeyToCMD( VK_ESCAPE, VS_ESCAPE, ESC, ENHANCED_KEY );
|
|
bRetVal = false;
|
|
m_dwInputSequneceState = IP_INIT;
|
|
}
|
|
break;
|
|
|
|
case IP_ESC_O_RCVD:
|
|
m_dwInputSequneceState = IP_INIT;
|
|
switch( cCurrentChar )
|
|
{
|
|
case 'P':
|
|
WriteAKeyToCMD( VK_F1, VS_F1, 0, ENHANCED_KEY );
|
|
break;
|
|
case 'Q':
|
|
WriteAKeyToCMD( VK_F2, VS_F2, 0, ENHANCED_KEY );
|
|
break;
|
|
case 'R':
|
|
WriteAKeyToCMD( VK_F3, VS_F3, 0, ENHANCED_KEY );
|
|
break;
|
|
case 'S':
|
|
WriteAKeyToCMD( VK_F4, VS_F4, 0, ENHANCED_KEY );
|
|
break;
|
|
default:
|
|
//Write already received escape as it is and return false which
|
|
//will make the current char also to be written to the cmd
|
|
WriteAKeyToCMD( VK_ESCAPE, VS_ESCAPE, ESC, ENHANCED_KEY );
|
|
WriteAKeyToCMD( 'O', VS_O, 'O', CAPSLOCK_ON ); //write O
|
|
bRetVal = false;
|
|
}
|
|
break;
|
|
|
|
case IP_ESC_BRACKET_RCVD:
|
|
m_dwInputSequneceState = IP_INIT;
|
|
switch( cCurrentChar )
|
|
{
|
|
case 'A':
|
|
WriteAKeyToCMD( VK_UP, VS_UP, 0, ENHANCED_KEY );
|
|
break;
|
|
|
|
case 'B':
|
|
WriteAKeyToCMD( VK_DOWN, VS_DOWN, 0, ENHANCED_KEY );
|
|
break;
|
|
|
|
case 'C':
|
|
WriteAKeyToCMD( VK_RIGHT, VS_RIGHT, 0, ENHANCED_KEY );
|
|
break;
|
|
|
|
case 'D':
|
|
WriteAKeyToCMD( VK_LEFT, VS_LEFT, 0, ENHANCED_KEY );
|
|
break;
|
|
case VT302_PAUSE: //'P'
|
|
WriteAKeyToCMD( VK_PAUSE, VS_PAUSE, 0, ENHANCED_KEY );
|
|
break;
|
|
|
|
default:
|
|
if( isdigit( cCurrentChar ) )
|
|
{
|
|
m_dwInputSequneceState = IP_ESC_BRACKET_DIGIT_RCVD;
|
|
m_dwDigitInTheSeq = cCurrentChar - '0';
|
|
}
|
|
else
|
|
{
|
|
WriteAKeyToCMD( VK_ESCAPE, VS_ESCAPE, ESC, ENHANCED_KEY );
|
|
WriteAKeyToCMD( VK_OEM_4, VS_OEM_4, '[', 0 );
|
|
bRetVal = false;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case IP_ESC_BRACKET_DIGIT_RCVD:
|
|
m_dwInputSequneceState = IP_INIT;
|
|
if( isdigit( cCurrentChar ) )
|
|
{
|
|
m_dwDigitInTheSeq = m_dwDigitInTheSeq * 10 + ( cCurrentChar - '0' ) ;
|
|
m_dwInputSequneceState = IP_ESC_BRACKET_DIGIT_RCVD;
|
|
}
|
|
else if( cCurrentChar == '~' )
|
|
{
|
|
switch( m_dwDigitInTheSeq )
|
|
{
|
|
case VT302_NEXT:
|
|
WriteAKeyToCMD( VK_NEXT, VS_NEXT, 0, ENHANCED_KEY );
|
|
break;
|
|
case VT302_PRIOR:
|
|
WriteAKeyToCMD( VK_PRIOR, VS_PRIOR, 0, ENHANCED_KEY );
|
|
break;
|
|
case VT302_END:
|
|
WriteAKeyToCMD( VK_END, VS_END, 0, ENHANCED_KEY );
|
|
break;
|
|
case VT302_INSERT:
|
|
WriteAKeyToCMD( VK_INSERT, VS_INSERT, 0, ENHANCED_KEY );
|
|
break;
|
|
case VT302_HOME:
|
|
WriteAKeyToCMD( VK_HOME, VS_HOME, 0, ENHANCED_KEY );
|
|
break;
|
|
case VT302_F5:
|
|
WriteAKeyToCMD( VK_F5, VS_F5, 0, ENHANCED_KEY );
|
|
break;
|
|
case VT302_F6:
|
|
WriteAKeyToCMD( VK_F6, VS_F6, 0, ENHANCED_KEY );
|
|
break;
|
|
case VT302_F7:
|
|
WriteAKeyToCMD( VK_F7, VS_F7, 0, ENHANCED_KEY );
|
|
break;
|
|
case VT302_F8:
|
|
WriteAKeyToCMD( VK_F8, VS_F8, 0, ENHANCED_KEY );
|
|
break;
|
|
case VT302_F9:
|
|
WriteAKeyToCMD( VK_F9, VS_F9, 0, ENHANCED_KEY );
|
|
break;
|
|
case VT302_F10:
|
|
WriteAKeyToCMD( VK_F10, VS_F10, 0, ENHANCED_KEY );
|
|
break;
|
|
case VT302_F11:
|
|
WriteAKeyToCMD( VK_F11, VS_F11, 0, ENHANCED_KEY );
|
|
break;
|
|
case VT302_F12:
|
|
WriteAKeyToCMD( VK_F12, VS_F12, 0, ENHANCED_KEY );
|
|
break;
|
|
default:
|
|
WriteAKeyToCMD( VK_ESCAPE, VS_ESCAPE, ESC, ENHANCED_KEY );
|
|
WriteAKeyToCMD( VK_OEM_4, VS_OEM_4, '[', 0 );
|
|
_chASSERT( 0 );
|
|
bRetVal = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WriteAKeyToCMD( VK_ESCAPE, VS_ESCAPE, ESC, ENHANCED_KEY );
|
|
WriteAKeyToCMD( VK_OEM_4, VS_OEM_4, '[', 0 );
|
|
_chASSERT( 0 );
|
|
bRetVal = false;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
//Should not happen
|
|
_chASSERT( 0 );
|
|
}
|
|
|
|
return bRetVal;
|
|
}
|
|
|
|
void
|
|
CScraper::SendChar( char ch )
|
|
{
|
|
SendBytes( ( PUCHAR )&ch, 1 );
|
|
}
|
|
|
|
void
|
|
CScraper::SendBytes( PUCHAR pucBuf, DWORD dwLength )
|
|
{
|
|
bool bSuccess=false;
|
|
bool bTransmitDone = false;
|
|
if( pucBuf == NULL )
|
|
return;
|
|
|
|
if( g_pucSendBuffer == NULL )
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if( g_dwSendBufferSize >= MAX_SOCKET_BUFFER_SIZE )
|
|
{
|
|
bSuccess = Transmit();
|
|
bTransmitDone = true;
|
|
}
|
|
if( ((bTransmitDone&&bSuccess)||(!bTransmitDone))&&( g_pucSendInsertPoint + dwLength ) > ( g_pucSendBuffer + g_dwSendBufferSize ) )
|
|
{
|
|
|
|
// do realloc
|
|
DWORD dwOffset = (DWORD)( g_pucSendInsertPoint - g_pucSendBuffer );
|
|
DWORD dwTmpBufSize = dwLength + dwOffset;
|
|
PUCHAR pTmpBuf = new UCHAR[dwTmpBufSize];
|
|
if( !pTmpBuf )
|
|
{
|
|
return;
|
|
}
|
|
|
|
memcpy( pTmpBuf, g_pucSendBuffer, dwOffset ); // No attack, calculated allocation already. Baskar.
|
|
delete [] g_pucSendBuffer;
|
|
g_pucSendBuffer = pTmpBuf;
|
|
|
|
g_dwSendBufferSize = dwTmpBufSize;
|
|
|
|
g_pucSendInsertPoint = g_pucSendBuffer + dwOffset;
|
|
}
|
|
}
|
|
if(g_pucSendInsertPoint)
|
|
{
|
|
memcpy( g_pucSendInsertPoint, pucBuf, dwLength ); // No size info for destination, Baskar - Attack ?
|
|
g_pucSendInsertPoint += dwLength;
|
|
}
|
|
}
|
|
|
|
void
|
|
CScraper::SendString( LPSTR pszString )
|
|
{
|
|
if( pszString == 0 )
|
|
return;
|
|
|
|
SendBytes( ( PUCHAR )pszString, strlen( pszString ) );
|
|
}
|
|
|
|
|
|
void
|
|
CScraper::SendFmt( LPSTR fmt, ... )
|
|
{
|
|
va_list arg;
|
|
CHAR szBuffer[ONE_KB + 1] = { 0 };
|
|
|
|
va_start( arg,fmt );
|
|
|
|
_vsnprintf( szBuffer, ONE_KB, fmt, arg );
|
|
|
|
va_end( arg );
|
|
|
|
SendString( szBuffer );
|
|
}
|
|
|
|
bool
|
|
CScraper::Transmit( )
|
|
{
|
|
if( g_pucSendInsertPoint != g_pucSendBuffer )
|
|
{
|
|
if( !TransmitBytes( g_pucSendBuffer, (DWORD)( g_pucSendInsertPoint - g_pucSendBuffer ) ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
//We need to tune this DEFAULT_SEND_BUFFER_SIZE
|
|
if( g_dwSendBufferSize > DEFAULT_SEND_BUFFER_SIZE )
|
|
{
|
|
//Don't keep if it is a huge buffer
|
|
delete [] g_pucSendBuffer;
|
|
g_dwSendBufferSize = 0;
|
|
if( !AllocateSendBuffer() )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
g_pucSendInsertPoint = g_pucSendBuffer;
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
bool
|
|
CScraper::TransmitBytes( PUCHAR pSendBuffer, DWORD dwSize)
|
|
{
|
|
bool bRetVal = TRUE;
|
|
|
|
DWORD dwCurrentPacket = 0;
|
|
DWORD dwTotalBytesSent = 0;
|
|
|
|
//StuffEsacapeIACs may put some escape chars. I am expecting atmost
|
|
//DELTA such chars. Typically there will be none. With out this, we
|
|
//have to make pass over the pOutBuf once just to find the right number
|
|
//in a routine that may be executed once every 100ms.
|
|
|
|
while( dwTotalBytesSent < dwSize )
|
|
{
|
|
( ( dwSize - dwTotalBytesSent ) >
|
|
( MAX_WRITE_SOCKET_BUFFER - DELTA ) )?
|
|
dwCurrentPacket = ( MAX_WRITE_SOCKET_BUFFER - DELTA ):
|
|
dwCurrentPacket = dwSize - dwTotalBytesSent;
|
|
|
|
DWORD dwNumBytesWritten = 0;
|
|
|
|
//Block until Previous Io is finished
|
|
if( !FinishIncompleteIo( (HANDLE) m_pSession->CIoHandler::m_sSocket,
|
|
&( m_pSession->CIoHandler::m_oWriteToSocket ), &dwNumBytesWritten ) )
|
|
{
|
|
bRetVal = FALSE ;
|
|
break;
|
|
}
|
|
|
|
DWORD dwCountWithEscapes = dwCurrentPacket;
|
|
StuffEscapeIACs( m_pSession->CIoHandler::m_WriteToSocketBuff,
|
|
pSendBuffer + dwTotalBytesSent, &dwCountWithEscapes );
|
|
m_pSession->CIoHandler::m_dwWriteToSocketIoLength=dwCountWithEscapes;
|
|
if( !m_pSession->CIoHandler::WriteToClient( ) )
|
|
{
|
|
bRetVal = FALSE ;
|
|
break;
|
|
}
|
|
dwTotalBytesSent += dwCurrentPacket;
|
|
}
|
|
|
|
return( bRetVal );
|
|
}
|
|
|
|
void
|
|
CScraper::SendColorInfo( WORD wAttributes )
|
|
{
|
|
if( wAttributes & BACKGROUND_INTENSITY )
|
|
{
|
|
//do nothing.
|
|
//There is no equivalent capability on vt100
|
|
}
|
|
if( wAttributes & FOREGROUND_INTENSITY )
|
|
{
|
|
if( !fBold )
|
|
{
|
|
_snprintf(szSGRStr, (sizeof(szSGRStr) - 1), "%c[%dm", 27, 1 ); //Bold
|
|
SendString( szSGRStr );
|
|
fBold = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( fBold )
|
|
{
|
|
_snprintf(szSGRStr, (sizeof(szSGRStr) - 1), "%c[%dm", 27, 22 ); //Bold off
|
|
SendString( szSGRStr );
|
|
fBold = false;
|
|
}
|
|
}
|
|
WORD wColor = 0;
|
|
if( wAttributes & FOREGROUND_BLUE )
|
|
{
|
|
wColor = ( WORD )( wColor | 0x0004 );
|
|
}
|
|
if( wAttributes & FOREGROUND_GREEN )
|
|
{
|
|
wColor = ( WORD )( wColor | 0x0002 );
|
|
}
|
|
if( wAttributes & FOREGROUND_RED )
|
|
{
|
|
wColor = ( WORD )( wColor | 0x0001 );
|
|
}
|
|
wColor += 30; //Base value for foreground colors
|
|
_snprintf(szSGRStr, (sizeof(szSGRStr) - 1), "%c[%dm", 27, wColor );
|
|
SendString( szSGRStr );
|
|
|
|
//WORD wColor = 0;
|
|
wColor = 0;
|
|
if( wAttributes & BACKGROUND_BLUE )
|
|
{
|
|
wColor = ( WORD )( wColor | 0x0004 );
|
|
}
|
|
if( wAttributes & BACKGROUND_GREEN )
|
|
{
|
|
wColor = ( WORD )( wColor | 0x0002 );
|
|
}
|
|
if( wAttributes & BACKGROUND_RED )
|
|
{
|
|
wColor = ( WORD )( wColor | 0x0001 );
|
|
}
|
|
wColor += 40; //Base value for Background colors
|
|
_snprintf(szSGRStr, (sizeof(szSGRStr) - 1), "%c[%dm", 27, wColor );
|
|
SendString( szSGRStr );
|
|
}
|
|
|
|
#define COMPARE_ROWS(currentRow, lastSeenRow, result) \
|
|
for(i = 0; i < wCols; ++i ) \
|
|
{ \
|
|
if( pCurrent[ ( currentRow ) * wCols + i].Char.AsciiChar != \
|
|
pLastSeen[ ( lastSeenRow ) * wCols + i].Char.AsciiChar ) \
|
|
{\
|
|
(result) = 0; \
|
|
break;\
|
|
} \
|
|
if( ( wDefaultAttributes != pCurrent[ ( currentRow ) * wCols + i]. \
|
|
Attributes ) && \
|
|
( pCurrent[ ( currentRow ) * wCols + i].Attributes != \
|
|
pLastSeen[ ( lastSeenRow ) * wCols + i].Attributes ) ) \
|
|
{ \
|
|
(result) = 0; \
|
|
break; \
|
|
} \
|
|
}
|
|
|
|
#define GET_DEFAULT_COLOR \
|
|
if( wDefaultAttributes == 0 ) \
|
|
{ \
|
|
wDefaultAttributes = pCurrent[ 0 ].Attributes; \
|
|
wExistingAttributes = pCurrent[ 0 ].Attributes; \
|
|
}
|
|
|
|
//row, column are over the wire should be w.r.t screen.
|
|
//So, +1 for both row, column
|
|
#define POSITION_CURSOR( row, column ) \
|
|
{ \
|
|
LPSTR lpszCMStr3 = pTermCap->CursorMove( lpszCMStr2, \
|
|
( WORD ) ( ( row ) + 1 ), \
|
|
( WORD ) ( ( column ) + 1 ) ); \
|
|
SendString( lpszCMStr3 ); \
|
|
delete [] lpszCMStr3; \
|
|
}
|
|
|
|
|
|
#define SEND_ROW( row, begin, end ) \
|
|
for(LONG c = ( begin ); c < ( end ); ++c ) \
|
|
{ \
|
|
if( wExistingAttributes != pCurrent[( row ) * wCols + c].Attributes ) \
|
|
{ \
|
|
wExistingAttributes = pCurrent[ ( row ) * wCols + c].Attributes; \
|
|
wDefaultAttributes = ( WORD )~0; \
|
|
SendColorInfo( wExistingAttributes ); \
|
|
} \
|
|
SendChar( pCurrent[ ( row ) * wCols + c].Char.AsciiChar ); \
|
|
}
|
|
|
|
#define IS_BLANK( row, col ) \
|
|
( pCurrent[ ( row ) * wCols + ( col ) ].Char.AsciiChar == ' ' )
|
|
|
|
#define IS_DIFFERENT_COLOR( row, col, attribs ) \
|
|
( pCurrent[ ( row ) * wCols + ( col ) ].Attributes != ( attribs ) )
|
|
|
|
#define IS_CHANGE_IN_COLOR( row, col ) \
|
|
( pCurrent[ ( row ) * wCols + ( col ) ].Attributes != \
|
|
pLastSeen[ ( row ) * wCols + ( col ) ].Attributes )
|
|
|
|
#define IS_CHANGE_IN_CHAR( row, col ) \
|
|
( pCurrent[ ( row ) * wCols + ( col ) ].Char.AsciiChar != \
|
|
pLastSeen[ ( row ) * wCols + ( col )].Char.AsciiChar )
|
|
|
|
|
|
bool
|
|
CScraper::CompareAndUpdateVT100( WORD wRows, WORD wCols, PCHAR_INFO pCurrent,
|
|
PCHAR_INFO pLastSeen,
|
|
PCONSOLE_SCREEN_BUFFER_INFO pCSBI,
|
|
PCONSOLE_SCREEN_BUFFER_INFO pLastCSBI,
|
|
bool *pfDifferenceFound )
|
|
{
|
|
_TRACE( TRACE_DEBUGGING, "CompareAndUpdateVT100()" );
|
|
|
|
INT i;
|
|
WORD wRow, wCol;
|
|
INT iStartCol,iEndCol;
|
|
bool fBlankLine;
|
|
|
|
COORD coordDest;
|
|
COORD coordOrigin;
|
|
|
|
SMALL_RECT srSource;
|
|
|
|
coordDest.X = wCols;
|
|
coordDest.Y = wRows;
|
|
coordOrigin.X = 0;
|
|
coordOrigin.Y = 0;
|
|
srSource.Left = 0;
|
|
srSource.Top = 0;
|
|
srSource.Right = ( WORD ) ( wCols - 1 );
|
|
srSource.Bottom = ( WORD ) ( wRows - 1 );
|
|
|
|
_chASSERT( pfDifferenceFound )
|
|
|
|
*pfDifferenceFound = false;
|
|
_chVERIFY2( m_hConBufOut = CreateFile( TEXT("CONOUT$"), GENERIC_READ |
|
|
GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL, 0 ) );
|
|
|
|
if( INVALID_HANDLE_VALUE == m_hConBufOut)
|
|
{
|
|
//Could be an application that does not share its screen buffer
|
|
return ( TRUE );
|
|
}
|
|
|
|
BOOL dwStatus = 0;
|
|
_chVERIFY2( dwStatus = ReadConsoleOutputA( m_hConBufOut, pCurrent, coordDest,
|
|
coordOrigin, &srSource ) );
|
|
if( !dwStatus )
|
|
{
|
|
LogFormattedGetLastError( EVENTLOG_ERROR_TYPE, MSG_ERR_READ_CONSOLE, GetLastError() );
|
|
TELNET_CLOSE_HANDLE( m_hConBufOut );
|
|
return ( FALSE );
|
|
}
|
|
|
|
// Read the current cursor position
|
|
_chVERIFY2( dwStatus = GetConsoleScreenBufferInfo( m_hConBufOut, pCSBI ) );
|
|
if( !dwStatus )
|
|
{
|
|
LogFormattedGetLastError( EVENTLOG_ERROR_TYPE, MSG_ERR_READ_CONSOLE, GetLastError() );
|
|
TELNET_CLOSE_HANDLE( m_hConBufOut );
|
|
return ( FALSE );
|
|
}
|
|
|
|
TELNET_CLOSE_HANDLE( m_hConBufOut );
|
|
|
|
GET_DEFAULT_COLOR;
|
|
|
|
//Check if the text was scrolled
|
|
LONG currentBuffBottom, lastSeenBuffBottom, currentBuffTop, lastSeenBuffTop;
|
|
LONG lastRow, currentRow;
|
|
bool checkRestOfBuffer;
|
|
bool isScrolled = 0;
|
|
|
|
currentBuffBottom = lastSeenBuffBottom = wRows - 1;
|
|
currentBuffTop = lastSeenBuffTop = 0;
|
|
|
|
//The following check is to prevent scrolling when the user hasn't pressed
|
|
//ENTER. This case is reproduced by typing the same command (for eg: dir in
|
|
//a directory with not too many files) many times until the screen is filled
|
|
//and then type one char extra or less for that command (For "dir", it is
|
|
//"di" or "dir /" ) then the screen scrolls
|
|
//The solution is to see if the only change since the last scraping is
|
|
//only one key stroke( one byte ), if so don't scroll.
|
|
//This may lead to the case that it may scroll when you press up arrow key
|
|
//( 3 bytes ).
|
|
//Note: This is may lead to scrolling when you press a single
|
|
//key stroke on a japanese machine. Multi byte chars etc.
|
|
|
|
if( !m_bCheckForScrolling )
|
|
{
|
|
isScrolled = 1;
|
|
}
|
|
m_bCheckForScrolling = 1;
|
|
|
|
//
|
|
//When scrolling occurs because of coincidental matching of blank lines at
|
|
//the bottom of the text in the cmd, it creates problems when clearing the
|
|
//screen. repro steps: goto last but one line in the client window.
|
|
//change directory. now list files. try clearing screen.
|
|
if( !( ( pLastCSBI->dwCursorPosition.Y == pCSBI->dwCursorPosition.Y ) &&
|
|
( pCSBI->dwCursorPosition.Y == pCSBI->srWindow.Bottom ) ) )
|
|
{
|
|
isScrolled = 1; //just to skip the following loop
|
|
}
|
|
while( currentBuffBottom >= currentBuffTop && !isScrolled )
|
|
{
|
|
checkRestOfBuffer = 1;
|
|
COMPARE_ROWS(currentBuffBottom, lastSeenBuffBottom, checkRestOfBuffer );
|
|
if ( checkRestOfBuffer )
|
|
{
|
|
isScrolled = 1;
|
|
if( currentBuffBottom > 0 )
|
|
{
|
|
//need not compare the same line again. Decrement by one.
|
|
currentRow = currentBuffBottom - 1;
|
|
lastRow = lastSeenBuffBottom - 1;
|
|
|
|
while( currentRow >= currentBuffTop && isScrolled )
|
|
{
|
|
COMPARE_ROWS(currentRow, lastRow, isScrolled );
|
|
currentRow--;
|
|
lastRow--;
|
|
}
|
|
}
|
|
}
|
|
|
|
currentBuffBottom--;
|
|
}
|
|
currentBuffBottom++;
|
|
|
|
//Patch to make this work for ONE blank line at the top
|
|
//Without this patch, screen scrolls for each char typed after "cls"
|
|
if( currentBuffBottom == currentBuffTop )
|
|
{
|
|
//check if it is a blank line. Then it is not scrolling.
|
|
for(i = 0; i < wCols; ++i )
|
|
{
|
|
if( !IS_BLANK( currentBuffTop , i ) )
|
|
{
|
|
break;
|
|
}
|
|
isScrolled = 0;
|
|
}
|
|
|
|
}
|
|
|
|
//if it is the same screen
|
|
if( currentBuffBottom + 1 >= wRows )
|
|
{
|
|
isScrolled = 0;
|
|
}
|
|
if( isScrolled )
|
|
{
|
|
//Now Send all the lines from currentBuffBottom
|
|
//till the end of the buffer
|
|
|
|
*pfDifferenceFound = true;
|
|
|
|
_TRACE( TRACE_DEBUGGING, "About to send from:%d till:%d",
|
|
currentBuffBottom + 1, wRows);
|
|
WORD lastChar = wCols;
|
|
|
|
for(LONG r = currentBuffBottom + 1; r < wRows ; r++)
|
|
{
|
|
SendChar( 0x0D );
|
|
SendChar( 0x0A );
|
|
|
|
lastChar = wCols;
|
|
while( lastChar > 0 && IS_BLANK( r, lastChar - 1 ) &&
|
|
!IS_DIFFERENT_COLOR( r, lastChar - 1, wExistingAttributes ) )
|
|
{
|
|
lastChar--;
|
|
}
|
|
|
|
SEND_ROW( r, 0, lastChar );
|
|
|
|
if( lastChar < wCols )
|
|
{
|
|
SendString( lpszCMStr1 );
|
|
}
|
|
}
|
|
//
|
|
//Do line by line scraping only if it was not scrolling
|
|
goto next;
|
|
}
|
|
|
|
_TRACE( TRACE_DEBUGGING, "NO scrolling");
|
|
// Search both the two-dimensional arrays for differences.
|
|
wRow = wCol = 0;
|
|
|
|
while( wRow < wRows )
|
|
{
|
|
if( memcmp( &pCurrent[wRow * wCols], &pLastSeen[wRow * wCols],
|
|
wCols * sizeof( CHAR_INFO ) ) != 0 )
|
|
{
|
|
// _TRACE( TRACE_DEBUGGING, "difference found in row %d\n", wRow );
|
|
*pfDifferenceFound = true;
|
|
|
|
iStartCol = -1;
|
|
iEndCol = -1;
|
|
fBlankLine = true;
|
|
|
|
for(i = 0 ; i < wCols; ++i )
|
|
{
|
|
if( IS_DIFFERENT_COLOR( wRow, i, wDefaultAttributes ) &&
|
|
IS_CHANGE_IN_COLOR( wRow, i ) )
|
|
{
|
|
if( iStartCol == -1 )
|
|
{
|
|
iStartCol = i;
|
|
}
|
|
iEndCol = i;
|
|
fBlankLine = false;
|
|
}
|
|
if( IS_CHANGE_IN_CHAR( wRow, i ) )
|
|
{
|
|
if( iStartCol == -1 )
|
|
{
|
|
iStartCol = i;
|
|
}
|
|
iEndCol = i;
|
|
}
|
|
if( fBlankLine && !IS_BLANK( wRow, i ) )
|
|
{
|
|
fBlankLine = false;
|
|
}
|
|
}
|
|
|
|
if( fBlankLine )
|
|
{
|
|
POSITION_CURSOR( wRow, 0 );
|
|
SendString( lpszCMStr1 );
|
|
|
|
coExpectedCursor.Y = wRow;
|
|
coExpectedCursor.X = 0;
|
|
}
|
|
else if( iStartCol != -1 )
|
|
{
|
|
if( wRow != coExpectedCursor.Y || iStartCol != coExpectedCursor.X )
|
|
{
|
|
POSITION_CURSOR( wRow, iStartCol );
|
|
|
|
coExpectedCursor.X = ( SHORT )iStartCol;
|
|
coExpectedCursor.Y = wRow;
|
|
}
|
|
|
|
SEND_ROW( wRow, iStartCol, iEndCol+1 );
|
|
|
|
coExpectedCursor.X = ( SHORT ) ( coExpectedCursor.X +
|
|
iEndCol - iStartCol + 1 );
|
|
}
|
|
}
|
|
++wRow;
|
|
}
|
|
|
|
next :
|
|
if( ( pCSBI->dwCursorPosition.X != coExpectedCursor.X ||
|
|
pCSBI->dwCursorPosition.Y != coExpectedCursor.Y ) &&
|
|
( memcmp( &pCSBI->dwCursorPosition, &pLastCSBI->dwCursorPosition,
|
|
sizeof( COORD ) ) != 0 || g_pucSendInsertPoint != g_pucSendBuffer ) )
|
|
{
|
|
POSITION_CURSOR( pCSBI->dwCursorPosition.Y,
|
|
pCSBI->dwCursorPosition.X );
|
|
*pfDifferenceFound = true;
|
|
|
|
coExpectedCursor.X = pCSBI->dwCursorPosition.X;
|
|
coExpectedCursor.Y = pCSBI->dwCursorPosition.Y;
|
|
}
|
|
|
|
if( *pfDifferenceFound )
|
|
{
|
|
_TRACE( TRACE_DEBUGGING, "difference found: %d \n", m_dwPollInterval );
|
|
// Copy pCurrent onto pLastSeen
|
|
memcpy( pLastSeen, pCurrent, wCols * wRows * sizeof( CHAR_INFO ) );
|
|
memcpy( pLastCSBI, pCSBI, sizeof( CONSOLE_SCREEN_BUFFER_INFO ) );
|
|
if( !Transmit( ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_TRACE( TRACE_DEBUGGING, "NO difference found: %d \n", m_dwPollInterval );
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
bool
|
|
CScraper::CompareAndUpdateVT80( WORD wRows, WORD wCols,
|
|
PCHAR_INFO pCurrent, PCHAR_INFO pLastSeen,
|
|
PCONSOLE_SCREEN_BUFFER_INFO pCSBI,
|
|
PCONSOLE_SCREEN_BUFFER_INFO pLastCSBI,
|
|
bool *pfDifferenceFound )
|
|
{
|
|
_TRACE( TRACE_DEBUGGING, "CompareAndUpdateVT80()" );
|
|
|
|
WORD i, j;
|
|
WORD wRow, wCol;
|
|
SHORT iStartCol,iEndCol;
|
|
bool fBlankLine;
|
|
bool fMatching;
|
|
|
|
COORD coordDest;
|
|
COORD coordOrigin;
|
|
|
|
SMALL_RECT srSource;
|
|
|
|
*pfDifferenceFound = false;
|
|
|
|
coordDest.X = wCols;
|
|
coordDest.Y = wRows;
|
|
coordOrigin.X = 0;
|
|
coordOrigin.Y = 0;
|
|
srSource.Left = 0;
|
|
srSource.Top = 0;
|
|
srSource.Right = ( WORD )( wCols - 1 );
|
|
srSource.Bottom = ( WORD )( wRows - 1 );
|
|
|
|
|
|
_chVERIFY2( m_hConBufOut = CreateFile( TEXT("CONOUT$"), GENERIC_READ |
|
|
GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL, 0 ) );
|
|
|
|
if( INVALID_HANDLE_VALUE == m_hConBufOut)
|
|
{
|
|
//Could be an application that does not share its screen buffer
|
|
return ( TRUE );
|
|
}
|
|
|
|
BOOL dwStatus = 0;
|
|
_chVERIFY2( dwStatus = ReadConsoleOutput( m_hConBufOut, pCurrent, coordDest,
|
|
coordOrigin, &srSource ) );
|
|
if( !dwStatus )
|
|
{
|
|
LogFormattedGetLastError( EVENTLOG_ERROR_TYPE, MSG_ERR_READ_CONSOLE, GetLastError() );
|
|
TELNET_CLOSE_HANDLE( m_hConBufOut );
|
|
return ( FALSE );
|
|
}
|
|
|
|
// Read the current cursor position
|
|
_chVERIFY2( dwStatus = GetConsoleScreenBufferInfo( m_hConBufOut, pCSBI ) );
|
|
if( !dwStatus )
|
|
{
|
|
LogFormattedGetLastError( EVENTLOG_ERROR_TYPE, MSG_ERR_READ_CONSOLE, GetLastError() );
|
|
TELNET_CLOSE_HANDLE( m_hConBufOut );
|
|
return ( FALSE );
|
|
}
|
|
|
|
TELNET_CLOSE_HANDLE( m_hConBufOut );
|
|
|
|
// Search both the two-dimensional arrays for differences.
|
|
fMatching = true;
|
|
wRow = wCol = 0;
|
|
|
|
bool bCurrentClippedLeadByte = false, bLastSeenClippedLeadByte = false;
|
|
|
|
while( wRow < wRows )
|
|
{
|
|
if( memcmp( &pCurrent[wRow * wCols], &pLastSeen[wRow * wCols],
|
|
wCols * sizeof( CHAR_INFO ) ) != 0 )
|
|
{
|
|
_TRACE( TRACE_DEBUGGING, "difference found in row %d\n", wRow );
|
|
*pfDifferenceFound = true;
|
|
|
|
iStartCol = -1;
|
|
iEndCol = -1;
|
|
fBlankLine = true;
|
|
|
|
for( i = 0, j = 0; (i < wCols) && (j < wCols); NULL )
|
|
{
|
|
if( (i == 0) && bCurrentClippedLeadByte )
|
|
{
|
|
++i;
|
|
bCurrentClippedLeadByte = false;
|
|
continue;
|
|
}
|
|
|
|
if( (j==0) && bLastSeenClippedLeadByte )
|
|
{
|
|
++j;
|
|
bLastSeenClippedLeadByte = false;
|
|
continue;
|
|
}
|
|
|
|
if( pCurrent[wRow * wCols + i].Char.UnicodeChar !=
|
|
pLastSeen[wRow * wCols + j].Char.UnicodeChar )
|
|
{
|
|
if( iStartCol == -1 )
|
|
{
|
|
iStartCol = i;
|
|
}
|
|
|
|
iEndCol = i;
|
|
}
|
|
|
|
if( (pCurrent[wRow * wCols + i].Char.UnicodeChar != L' ') )
|
|
{
|
|
fBlankLine = false;
|
|
}
|
|
|
|
if( pCurrent[wRow * wCols + i].Attributes & COMMON_LVB_LEADING_BYTE )
|
|
{
|
|
i += 2;
|
|
}
|
|
else
|
|
{
|
|
++i;
|
|
}
|
|
|
|
if( pLastSeen[wRow * wCols + j].Attributes & COMMON_LVB_LEADING_BYTE )
|
|
{
|
|
j += 2;
|
|
}
|
|
else
|
|
{
|
|
++j;
|
|
}
|
|
|
|
if( (j >= wCols) && (i < wCols) )
|
|
{
|
|
(i == wCols) ? (i = ( WORD ) (wCols-1)) : NULL;
|
|
|
|
if( iStartCol == -1 )
|
|
{
|
|
iStartCol = i;
|
|
}
|
|
iEndCol = ( WORD ) ( wCols - 1 );
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( pCurrent[wRow * wCols + (wCols - 1)].Attributes & COMMON_LVB_LEADING_BYTE )
|
|
{
|
|
bCurrentClippedLeadByte = true;
|
|
}
|
|
|
|
if( pLastSeen[wRow * wCols + (wCols - 1)].Attributes & COMMON_LVB_LEADING_BYTE )
|
|
{
|
|
bLastSeenClippedLeadByte = true;
|
|
}
|
|
|
|
|
|
if( fBlankLine )
|
|
{
|
|
LPSTR lpszCMStr3 = pTermCap->CursorMove( lpszCMStr2,
|
|
( WORD )( wRow + 1 ), 1 );
|
|
SendString( lpszCMStr3 );
|
|
delete [] lpszCMStr3;
|
|
|
|
SendString( lpszCMStr1 );
|
|
}
|
|
else
|
|
{
|
|
if( iStartCol != -1 )
|
|
{
|
|
LPSTR lpszCMStr3 = pTermCap->CursorMove( lpszCMStr2,
|
|
( WORD ) ( wRow + 1 ) ,
|
|
( WORD ) ( iStartCol + 1 ) );
|
|
SendString( lpszCMStr3 );
|
|
delete [] lpszCMStr3;
|
|
|
|
for( wCol = iStartCol; wCol <= iEndCol; ++wCol )
|
|
{
|
|
CHAR mbCurrent[5];
|
|
int ret, xy;
|
|
WCHAR wcBuf[2];
|
|
|
|
wcBuf[0] = pCurrent[wRow * wCols + wCol].Char.UnicodeChar;
|
|
wcBuf[1] = L'\0';
|
|
|
|
ret = WideCharToMultiByte( CP_ACP, 0, wcBuf, -1, mbCurrent, 4, NULL, NULL );
|
|
|
|
if( (ret != 0) )
|
|
{
|
|
//if( pCurrent[ wRow * wCols + wCol].Char.AsciiChar > 31 )
|
|
//SendChar( pCurrent[wRow * wCols + wCol].Char.AsciiChar );
|
|
for( xy=0; xy < ret; ++xy)
|
|
{
|
|
SendChar( mbCurrent[xy] );
|
|
}
|
|
}
|
|
|
|
if( pCurrent[wRow * wCols + wCol].Attributes & COMMON_LVB_LEADING_BYTE )
|
|
{
|
|
++wCol;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
++wRow;
|
|
}
|
|
|
|
if( memcmp( &pCSBI->dwCursorPosition, &pLastCSBI->dwCursorPosition,
|
|
sizeof( COORD ) ) != 0 || g_pucSendInsertPoint != g_pucSendBuffer )
|
|
{
|
|
LPSTR lpszCMStr3 = pTermCap->CursorMove( lpszCMStr2,
|
|
( WORD ) ( pCSBI->dwCursorPosition.Y + 1 ),
|
|
( WORD ) ( pCSBI->dwCursorPosition.X + 1 ));
|
|
SendString( lpszCMStr3 );
|
|
delete [] lpszCMStr3;
|
|
|
|
*pfDifferenceFound = true;
|
|
}
|
|
|
|
if( *pfDifferenceFound )
|
|
{
|
|
// Copy pCurrent onto pLastSeen
|
|
memcpy( pLastSeen, pCurrent, wCols * wRows * sizeof( CHAR_INFO ) );
|
|
memcpy( pLastCSBI, pCSBI, sizeof( CONSOLE_SCREEN_BUFFER_INFO ) );
|
|
if( !Transmit( ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_TRACE( TRACE_DEBUGGING, "NO difference found\n" );
|
|
}
|
|
return( TRUE );
|
|
}
|
|
|
|
bool
|
|
CScraper::GetRegistryValues( void )
|
|
{
|
|
HKEY hk;
|
|
bool success;
|
|
DWORD dwDisp = 0;
|
|
|
|
if ( TnSecureRegCreateKeyEx( HKEY_LOCAL_MACHINE, REG_PARAMS_KEY, NULL, NULL,
|
|
REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED , NULL, &hk, &dwDisp, 0) )
|
|
{
|
|
LogEvent( EVENTLOG_ERROR_TYPE, MSG_REGISTRYKEY, REG_PARAMS_KEY );
|
|
return( FALSE );
|
|
}
|
|
|
|
HKEY hk1;
|
|
LPTSTR lpszTelnetInstallPath = NULL;
|
|
if( !TnSecureRegCreateKeyEx( HKEY_LOCAL_MACHINE, REG_SERVICE_KEY, NULL, NULL,
|
|
REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED , NULL, &hk1, &dwDisp, 0 ) )
|
|
{
|
|
success = GetRegistryString( hk1, NULL, L"ImagePath", &lpszTelnetInstallPath,
|
|
L"",FALSE );
|
|
RegCloseKey( hk1 );
|
|
|
|
if (! success)
|
|
{
|
|
RegCloseKey(hk);
|
|
return( FALSE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RegCloseKey(hk);
|
|
LogEvent( EVENTLOG_ERROR_TYPE, MSG_REGISTRYKEY, REG_SERVICE_KEY );
|
|
return (FALSE);
|
|
}
|
|
|
|
WCHAR *pLastBackSlash = wcsrchr( lpszTelnetInstallPath, L'\\' );
|
|
if( pLastBackSlash != NULL )
|
|
{
|
|
*pLastBackSlash = 0;
|
|
}
|
|
LPWSTR lpszDefaultTermcapFullPathName =
|
|
new WCHAR[ ( wcslen( lpszTelnetInstallPath ) +
|
|
wcslen( DEFAULT_TERMCAP ) + 2 ) ];
|
|
if( !lpszDefaultTermcapFullPathName )
|
|
{
|
|
RegCloseKey(hk);
|
|
return( FALSE );
|
|
}
|
|
|
|
wcscpy( lpszDefaultTermcapFullPathName, lpszTelnetInstallPath );
|
|
wcscat( lpszDefaultTermcapFullPathName, L"\\" );
|
|
wcscat( lpszDefaultTermcapFullPathName, DEFAULT_TERMCAP );
|
|
LPWSTR pszFileName;
|
|
success = GetRegistryString( hk, NULL, L"Termcap", &pszFileName,
|
|
lpszDefaultTermcapFullPathName,FALSE );
|
|
|
|
RegCloseKey(hk);
|
|
|
|
if (! success)
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
WCHAR szCmdBuf[ONE_KB];
|
|
ExpandEnvironmentStrings( pszFileName, szCmdBuf, (sizeof(szCmdBuf)/sizeof(szCmdBuf[0])) );
|
|
|
|
delete [] lpszDefaultTermcapFullPathName;
|
|
delete [] lpszTelnetInstallPath;
|
|
delete [] pszFileName;
|
|
|
|
CTermCap::m_pszFileName = new CHAR[ wcslen(szCmdBuf) +1];
|
|
if( !CTermCap::m_pszFileName )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
_snprintf( CTermCap::m_pszFileName, ONE_KB -1 ,"%lS", szCmdBuf ); // No attack, we allocate the necesary buffer here - Baskar.
|
|
return ( TRUE );
|
|
}
|
|
|
|
bool
|
|
CScraper::CompareAndUpdateVTNT ( WORD wRows, WORD wCols, PCHAR_INFO pCurrent,
|
|
PCHAR_INFO pLastSeen,
|
|
PCONSOLE_SCREEN_BUFFER_INFO pCSBI,
|
|
PCONSOLE_SCREEN_BUFFER_INFO pLastCSBI,
|
|
bool *pfDifferenceFound )
|
|
|
|
{
|
|
_TRACE( TRACE_DEBUGGING, "CompareAndUpdateVTNT()" );
|
|
|
|
INT result;
|
|
|
|
COORD coordDest;
|
|
COORD coSize;
|
|
|
|
SMALL_RECT srSource;
|
|
|
|
*pfDifferenceFound = false;
|
|
|
|
coordDest.X = wCols;
|
|
coordDest.Y = wRows;
|
|
|
|
coSize.X = 0;
|
|
coSize.Y = 0;
|
|
|
|
srSource.Left = 0;
|
|
srSource.Top = 0;
|
|
srSource.Right = ( WORD ) ( wCols - 1 );
|
|
srSource.Bottom = ( WORD ) ( wRows - 1 );
|
|
|
|
|
|
SHORT loop1;
|
|
SHORT loop2;
|
|
DWORD dwLineSize;
|
|
LPSTR lpCurrentWindow;
|
|
LPSTR lpLastSeenWindow;
|
|
LPSTR lpCurrentFirstLine;
|
|
LPSTR lpLastSeenFirstLine;
|
|
|
|
_chVERIFY2( m_hConBufOut = CreateFile( TEXT("CONOUT$"), GENERIC_READ |
|
|
GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL, 0 ) );
|
|
|
|
if( INVALID_HANDLE_VALUE == m_hConBufOut)
|
|
{
|
|
//Could be an application that does not share its screen buffer
|
|
return ( TRUE );
|
|
}
|
|
|
|
BOOL dwStatus = 0;
|
|
_chVERIFY2( dwStatus = ReadConsoleOutput( m_hConBufOut, pCurrent, coordDest,
|
|
coSize, &srSource ) );
|
|
if( !dwStatus )
|
|
{
|
|
LogFormattedGetLastError( EVENTLOG_ERROR_TYPE, MSG_ERR_READ_CONSOLE, GetLastError() );
|
|
TELNET_CLOSE_HANDLE( m_hConBufOut );
|
|
return ( FALSE );
|
|
}
|
|
|
|
_chVERIFY2( dwStatus = GetConsoleScreenBufferInfo( m_hConBufOut, pCSBI ) );
|
|
if( !dwStatus )
|
|
{
|
|
LogFormattedGetLastError( EVENTLOG_ERROR_TYPE, MSG_ERR_READ_CONSOLE, GetLastError() );
|
|
TELNET_CLOSE_HANDLE( m_hConBufOut );
|
|
return ( FALSE );
|
|
}
|
|
|
|
TELNET_CLOSE_HANDLE( m_hConBufOut );
|
|
|
|
dwLineSize = wCols * sizeof( CHAR_INFO ) ;
|
|
|
|
//VERSION2 : to optimize we can compare the whole window
|
|
//and if no change, return
|
|
|
|
LONG currentBuffBottom, lastSeenBuffBottom, currentBuffTop, lastSeenBuffTop;
|
|
LONG lastRow, currentRow;
|
|
bool checkRestOfBuffer;
|
|
bool isScrolled = 0;
|
|
int i = 0;
|
|
|
|
currentBuffBottom = lastSeenBuffBottom = wRows - 1;
|
|
currentBuffTop = lastSeenBuffTop = 0;
|
|
|
|
//The following check is to prevent scrolling when the user hasn't pressed
|
|
//ENTER. This case is reproduced by typing the same command (for eg: dir in
|
|
//a directory with not too many files) many times until the screen is filled
|
|
//and then type one char extra or less for that command (For "dir", it is
|
|
//"di" or "dir /" ) then the screen scrolls
|
|
//The solution is to see if the only change since the last scraping is
|
|
//only one key stroke( one byte ), if so don't scroll.
|
|
//This may lead to the case that it may scroll when you press up arrow key
|
|
//( 3 bytes ).
|
|
|
|
if( !m_bCheckForScrolling )
|
|
{
|
|
isScrolled = 1;
|
|
}
|
|
m_bCheckForScrolling = 1;
|
|
|
|
if( !m_pSession->CSession::m_bIsTelnetVersion2 )
|
|
{
|
|
//This will make it a non scrolling case.
|
|
isScrolled = 1;
|
|
}
|
|
|
|
while( currentBuffBottom >= currentBuffTop && !isScrolled )
|
|
{
|
|
checkRestOfBuffer = 1;
|
|
|
|
lpCurrentWindow = ( LPSTR ) ( pCurrent + wCols * currentBuffBottom ) ;
|
|
lpLastSeenWindow = ( LPSTR ) ( pLastSeen + wCols * lastSeenBuffBottom ) ;
|
|
|
|
if( memcmp( lpCurrentWindow, lpLastSeenWindow, dwLineSize ) != 0 )
|
|
{
|
|
checkRestOfBuffer = 0;
|
|
}
|
|
|
|
if ( checkRestOfBuffer )
|
|
{
|
|
isScrolled = 1;
|
|
if( currentBuffBottom > 0 )
|
|
{
|
|
//need not compare the same line again. Decrement by one.
|
|
currentRow = currentBuffBottom - 1;
|
|
lastRow = lastSeenBuffBottom - 1;
|
|
|
|
while( currentRow >= currentBuffTop && isScrolled )
|
|
{
|
|
lpCurrentWindow = ( LPSTR ) ( pCurrent + wCols * currentRow ) ;
|
|
lpLastSeenWindow = ( LPSTR ) ( pLastSeen + wCols * lastRow ) ;
|
|
|
|
if( memcmp( lpCurrentWindow, lpLastSeenWindow, dwLineSize ) != 0 )
|
|
{
|
|
isScrolled = 0;
|
|
}
|
|
currentRow--;
|
|
lastRow--;
|
|
}
|
|
}
|
|
}
|
|
|
|
currentBuffBottom--;
|
|
}
|
|
currentBuffBottom++;
|
|
|
|
//Patch to make this work for ONE blank line at the top
|
|
//Without this patch, screen scrolls for each char typed after "cls"
|
|
if( currentBuffBottom == currentBuffTop )
|
|
{
|
|
//check if it is a blank line. Then it is not scrolling.
|
|
for(i = 0; i < wCols; ++i )
|
|
{
|
|
if( !IS_BLANK( currentBuffTop , i ) )
|
|
{
|
|
break;
|
|
}
|
|
isScrolled = 0;
|
|
}
|
|
}
|
|
|
|
//if it is the same screen
|
|
if( currentBuffBottom + 1 >= wRows )
|
|
{
|
|
isScrolled = 0;
|
|
}
|
|
if( isScrolled )
|
|
{
|
|
//Now Send all the lines from currentBuffBottom
|
|
//till the end of the buffer
|
|
|
|
COORD coordSaveCursorPos = {0,0};
|
|
*pfDifferenceFound = true;
|
|
|
|
_TRACE( TRACE_DEBUGGING, "About to send from:%d till:%d",
|
|
currentBuffBottom + 1, wRows);
|
|
WORD lastChar = wCols;
|
|
|
|
|
|
// Determine size of changes
|
|
coSize.X = wCols;
|
|
coSize.Y = ( SHORT ) ( ( wRows - 1 ) - currentBuffBottom );
|
|
|
|
coordSaveCursorPos.X = pCSBI->dwCursorPosition.X;
|
|
coordSaveCursorPos.Y = pCSBI->dwCursorPosition.Y;
|
|
|
|
SendVTNTData( RELATIVE_COORDS, coordSaveCursorPos, coSize,
|
|
&srSource, pCurrent + (currentBuffBottom+1) * wCols );
|
|
|
|
//
|
|
//Do line by line scraping only if it was not scrolling
|
|
goto next1;
|
|
}
|
|
|
|
_TRACE( TRACE_DEBUGGING, "NO scrolling");
|
|
|
|
// Compare all lines
|
|
lpCurrentWindow = ( LPSTR ) pCurrent;
|
|
lpLastSeenWindow = ( LPSTR ) pLastSeen;
|
|
|
|
for( loop1 = 0 ; loop1 < wRows; )
|
|
{
|
|
result = memcmp( lpCurrentWindow, lpLastSeenWindow, dwLineSize );
|
|
|
|
loop1++;
|
|
|
|
lpCurrentFirstLine = lpCurrentWindow;
|
|
lpLastSeenFirstLine = lpLastSeenWindow;
|
|
|
|
lpLastSeenWindow += dwLineSize;
|
|
lpCurrentWindow += dwLineSize;
|
|
|
|
if( !result )
|
|
continue;
|
|
|
|
*pfDifferenceFound = true;
|
|
|
|
for( loop2 = loop1 ; loop2 < wRows ; loop2++ )
|
|
{
|
|
if( !memcmp ( lpLastSeenWindow, lpCurrentWindow, dwLineSize ) )
|
|
break ;
|
|
|
|
lpLastSeenWindow += dwLineSize;
|
|
lpCurrentWindow += dwLineSize;
|
|
}
|
|
|
|
if( loop2 == wRows )
|
|
{
|
|
lpLastSeenWindow += dwLineSize ;
|
|
lpCurrentWindow += dwLineSize ;
|
|
}
|
|
|
|
// Determine size of changes
|
|
coSize.X = wCols;
|
|
coSize.Y = ( WORD ) ( loop2 - loop1 + 1 );
|
|
|
|
srSource.Left = 0;
|
|
srSource.Top = ( WORD )( 0 + loop1 - 1 );
|
|
srSource.Right = ( WORD )( wCols - 1 );
|
|
srSource.Bottom = ( WORD ) ( srSource.Top + coSize.Y - 1 );
|
|
|
|
_TRACE( TRACE_DEBUGGING, "Scrape rom row %d to row %d", loop1, loop2 );
|
|
if( !SendVTNTData( ABSOLUTE_COORDS, pCSBI->dwCursorPosition, coSize,
|
|
&srSource, ( PCHAR_INFO ) lpCurrentFirstLine ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
loop1 = loop2 ;
|
|
}
|
|
|
|
next1:
|
|
if( !( *pfDifferenceFound ) )
|
|
{
|
|
if( memcmp( &pCSBI->dwCursorPosition, &pLastCSBI->dwCursorPosition,
|
|
sizeof( COORD ) ) != 0 )
|
|
{
|
|
|
|
*pfDifferenceFound = true;
|
|
|
|
//sending no data; only pCSBI->dwCursorPosition are valid
|
|
//(useful)
|
|
coSize.X = coSize.Y = 0;
|
|
srSource.Top = srSource.Bottom = srSource.Left = srSource.Right = 0;
|
|
if( !SendVTNTData( ABSOLUTE_COORDS, pCSBI->dwCursorPosition, coSize,
|
|
&srSource, NULL ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
}
|
|
|
|
if( *pfDifferenceFound )
|
|
{
|
|
if( !Transmit() )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// Copy pCurrent onto pLastSeen
|
|
memcpy( pLastSeen, pCurrent, wCols * wRows * sizeof( CHAR_INFO ) );
|
|
memcpy( pLastCSBI, pCSBI, sizeof( CONSOLE_SCREEN_BUFFER_INFO ) );
|
|
|
|
}
|
|
else
|
|
{
|
|
_TRACE( TRACE_DEBUGGING, "NO difference found\n" );
|
|
}
|
|
return( TRUE );
|
|
}
|
|
|
|
bool
|
|
CScraper::SendVTNTData( WORD wCoordType,
|
|
COORD coCursor, COORD coRegionSize,
|
|
SMALL_RECT* lpScreenRegion, CHAR_INFO* lpScreenRegionCharInfo )
|
|
{
|
|
bool bRetVal = TRUE;
|
|
VTNT_CHAR_INFO pOutCharInfo;
|
|
DWORD dwSize = sizeof( VTNT_CHAR_INFO ) +
|
|
( coRegionSize.X * coRegionSize.Y * sizeof( CHAR_INFO ) );
|
|
SfuZeroMemory(&pOutCharInfo,sizeof(VTNT_CHAR_INFO));
|
|
//csbi.wAttributes is filled by v2 server with following meaning
|
|
//When a scrolling case is detected, this is set to 1.
|
|
pOutCharInfo.csbi.wAttributes = wCoordType;
|
|
pOutCharInfo.coDest.X = 0;
|
|
pOutCharInfo.coDest.Y = 0;
|
|
pOutCharInfo.coCursorPos = coCursor;
|
|
pOutCharInfo.coSizeOfData = coRegionSize;
|
|
pOutCharInfo.srDestRegion = *lpScreenRegion;
|
|
|
|
SendBytes( ( PUCHAR ) &pOutCharInfo, sizeof( VTNT_CHAR_INFO ) );
|
|
SendBytes( ( PUCHAR ) lpScreenRegionCharInfo, dwSize - sizeof( VTNT_CHAR_INFO) );
|
|
|
|
return( bRetVal );
|
|
}
|
|
|
|
void
|
|
CScraper::WriteMessageToCmd( WCHAR szMsg[] )
|
|
{
|
|
DWORD dwWritten = 0;
|
|
|
|
if( m_pSession->CIoHandler::m_SocketControlState == m_pSession->CIoHandler::STATE_SESSION )
|
|
{
|
|
_chVERIFY2( m_hConBufOut = CreateFile( TEXT("CONOUT$"), GENERIC_READ |
|
|
GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL, 0 ) );
|
|
|
|
if( INVALID_HANDLE_VALUE == m_hConBufOut)
|
|
{
|
|
LogFormattedGetLastError( EVENTLOG_ERROR_TYPE, MSG_ERRCONSOLE, GetLastError() );
|
|
return;
|
|
}
|
|
|
|
_chVERIFY2( WriteConsole( m_hConBufOut, szMsg, wcslen( szMsg ), &dwWritten, NULL ) );
|
|
TELNET_CLOSE_HANDLE( m_hConBufOut );
|
|
}
|
|
return;
|
|
}
|
|
|