Leaked source code of windows server 2003
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.
 
 
 
 
 
 

5629 lines
147 KiB

/*++
* File name:
* scfuncs.cpp
* Contents:
* Functions exported to smclient intepreter
*
* Copyright (C) 1998-1999 Microsoft Corp.
--*/
#pragma warning(disable:4152) // nonstandard extension, function/data pointer
// conversion in expression
#pragma warning(disable:4201) // nonstandard extension used : nameless
// struct/union
#pragma warning(disable:4706) // assignment within conditional expression
#include <windows.h>
#include <stdio.h>
#include <malloc.h>
#include <process.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdlib.h>
#include <io.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <tchar.h>
#include <winscard.h>
#include <winsock.h>
#include "tclient.h"
#define PROTOCOLAPI // __declspec(dllexport)
#include "protocol.h"
#include "extraexp.h"
#include "gdata.h"
#include "queues.h"
#include "misc.h"
#ifdef _RCLX
#include "rclx.h"
#endif // _RCLX
#include "sccons.h"
#include "scfuncs.h"
#define TSFLAG_CONSOLE 8
// This structure is used by _FindTopWindow
typedef struct _SEARCHWND {
TCHAR *szClassName; // The class name of searched window,
// NULL - ignore
TCHAR *szCaption; // Window caption, NULL - ignore
LONG_PTR lProcessId; // Process Id of the owner, 0 - ignore
HWND hWnd; // Found window handle
} SEARCHWND, *PSEARCHWND;
//
// type of imported functions, see check("call...") statement
//
typedef LPCSTR (_cdecl *PFNSMCDLLIMPORT)( PVOID, LPCWSTR );
//
// Function pointers for smarcard APIs. These are not supported on versions
// of Windows before Windows 95 OSR2 or Windows NT 4.0 SP3, so are loaded at
// runtime.
//
#define SMARTCARD_LIBRARY TEXT("winscard.dll")
HMODULE g_hSmartcardLibrary;
#define SCARDESTABLISHCONTEXT "SCardEstablishContext"
LONG
(WINAPI
*g_pfnSCardEstablishContext)(
DWORD,
LPCVOID,
LPCVOID,
LPSCARDCONTEXT
);
#ifdef UNICODE
#define SCARDLISTREADERS "SCardListReadersW"
#else
#define SCARDLISTREADERS "SCardListReadersA"
#endif
LONG
(WINAPI
*g_pfnSCardListReaders)(
SCARDCONTEXT,
LPCTSTR,
LPTSTR,
LPDWORD
);
#ifdef UNICODE
#define SCARDGETSTATUSCHANGE "SCardGetStatusChangeW"
#else
#define SCARDGETSTATUSCHANGE "SCardGetStatusChangeA"
#endif
LONG
(WINAPI
*g_pfnSCardGetStatusChange)(
SCARDCONTEXT,
DWORD,
LPSCARD_READERSTATE,
DWORD
);
#define SCARDFREEMEMORY "SCardFreeMemory"
LONG
(WINAPI
*g_pfnSCardFreeMemory)(
SCARDCONTEXT,
LPCVOID
);
#define SCARDRELEASECONTEXT "SCardReleaseContext"
LONG
(WINAPI
*g_pfnSCardReleaseContext)(
IN SCARDCONTEXT
);
/*++
* Function:
* SCInit
* Description:
* Called by smclient after the library is loaded.
* Passes trace routine
* Arguments:
* pInitData - contains a trace routine
* Called by:
* !smclient
--*/
PROTOCOLAPI
VOID
SMCAPI
SCInit(SCINITDATA *pInitData)
{
g_pfnPrintMessage = pInitData->pfnPrintMessage;
}
/*++
* Function:
* SCConnectEx
* Description:
* Called by smclient when connect command is interpreted
* Arguments:
* lpszServerName - server to connect to
* lpszUserName - login user name. Empty string means no login
* lpszPassword - login password
* lpszDomain - login domain, empty string means login to a domain
* the same as lpszServerName
* xRes, yRes - clients resolution, 0x0 - default
* ConnectFlags -
* - low speed (compression) option
* - cache the bitmaps to the disc option
* - connection context allocated in this function
* Return value:
* Error message. NULL on success
* Called by:
* SCConnect
--*/
PROTOCOLAPI
LPCSTR
SMCAPI
SCConnectEx(
LPCWSTR lpszServerName,
LPCWSTR lpszUserName,
LPCWSTR lpszPassword,
LPCWSTR lpszDomain,
LPCWSTR lpszShell,
int xRes,
int yRes,
int ConnectionFlags,
int Bpp,
int AudioOpts,
PCONNECTINFO *ppCI)
{
// HWND hDialog;
HWND hClient;
// HWND hConnect;
HWND hContainer, hInput, hOutput;
STARTUPINFO si;
PROCESS_INFORMATION procinfo;
LPCSTR rv = NULL;
int trys;
WCHAR szCommandLine[ 4 * MAX_STRING_LENGTH ];
LPCSTR szDiscon;
UINT xxRes, yyRes;
CHAR myServerName[ MAX_STRING_LENGTH ];
// Correct the resolution
if (xRes >= 1600 && yRes >= 1200) {xxRes = 1600; yyRes = 1200;}
else if (xRes >= 1280 && yRes >= 1024) {xxRes = 1280; yyRes = 1024;}
else if (xRes >= 1024 && yRes >= 768) {xxRes = 1024; yyRes = 768;}
else if (xRes >= 800 && yRes >= 600) {xxRes = 800; yyRes = 600;}
else {xxRes = 640; yyRes = 480;}
*ppCI = NULL;
for (trys = 60; trys && !g_hWindow; trys--)
Sleep(1000);
if (!g_hWindow)
{
TRACE((ERROR_MESSAGE, "Panic !!! Feedback window is not created\n"));
rv = ERR_WAIT_FAIL_TIMEOUT;
goto exitpt;
}
*ppCI = (PCONNECTINFO)malloc(sizeof(**ppCI));
if (!*ppCI)
{
TRACE((ERROR_MESSAGE,
"Couldn't allocate %d bytes memory\n",
sizeof(**ppCI)));
rv = ERR_ALLOCATING_MEMORY;
goto exitpt;
}
memset(*ppCI, 0, sizeof(**ppCI));
(*ppCI)->pConfigInfo = (PCONFIGINFO)malloc(sizeof(CONFIGINFO));
if (!((*ppCI)->pConfigInfo))
{
TRACE((ERROR_MESSAGE,
"Couldn't allocate %d bytes memory\n");
sizeof(**ppCI));
rv = ERR_ALLOCATING_MEMORY;
goto exiterr;
}
WideCharToMultiByte(CP_ACP,0,lpszServerName,-1,myServerName, sizeof( myServerName ), NULL, NULL);
_FillConfigInfo((*ppCI)->pConfigInfo);
//
// check for console extension
//
if ( 0 != ( ConnectionFlags & TSFLAG_CONSOLE ))
{
(*ppCI)->bConsole = TRUE;
rv = _SCConsConnect(
lpszServerName,
lpszUserName,
lpszPassword,
lpszDomain,
xRes, yRes,
*ppCI
);
if ( NULL == rv )
goto exitpt;
else {
//
// the trick here is the console will be unloaded after
// several clock ticks, so we need to replace the error with
// generic one
//
TRACE((ERROR_MESSAGE, "Error in console dll (replacing): %s\n", rv ));
rv = ERR_CONSOLE_GENERIC;
goto exiterr;
}
}
(*ppCI)->OwnerThreadId = GetCurrentThreadId();
// Check in what mode the client will be executed
// if the server name starts with '\'
// then tclient.dll will wait until some remote client
// is connected (aka RCLX mode)
// otherwise start the client on the same machine
// running tclient.dll (smclient)
if (*lpszServerName != L'\\')
{
// This is local mode, start the RDP client process
FillMemory(&si, sizeof(si), 0);
si.cb = sizeof(si);
si.wShowWindow = SW_SHOWMINIMIZED;
SetAllowBackgroundInput();
if ( (*ppCI)->pConfigInfo->UseRegistry )
_SetClientRegistry(lpszServerName,
lpszShell,
lpszUserName,
lpszPassword,
lpszDomain,
xxRes, yyRes,
Bpp,
AudioOpts,
ppCI,
ConnectionFlags,
(*ppCI)->pConfigInfo->KeyboardHook);
if ( 0 != wcslen( (*ppCI)->pConfigInfo->strCmdLineFmt ))
{
ConstructCmdLine(
lpszServerName,
lpszUserName,
lpszPassword,
lpszDomain,
lpszShell,
xRes,
yRes,
ConnectionFlags,
szCommandLine,
sizeof( szCommandLine ) / sizeof( *szCommandLine) - 1,
(*ppCI)->pConfigInfo
);
}
else {
_snwprintf(szCommandLine, sizeof(szCommandLine)/sizeof(WCHAR),
#ifdef _WIN64
L"%s /CLXDLL=CLXTSHAR.DLL /CLXCMDLINE=%s%I64d %s " REG_FORMAT,
#else // !_WIN64
L"%s /CLXDLL=CLXTSHAR.DLL /CLXCMDLINE=%s%d %s " REG_FORMAT,
#endif // _WIN64
(*ppCI)->pConfigInfo->strClientImg, _T(_HWNDOPT),
(LONG_PTR)g_hWindow,
(ConnectionFlags & TSFLAG_RCONSOLE)?L"-console":L"",
GetCurrentProcessId(), GetCurrentThreadId());
}
szCommandLine[ sizeof(szCommandLine)/sizeof(WCHAR) - 1 ] = 0;
(*ppCI)->dead = FALSE;
_AddToClientQ(*ppCI);
if (!CreateProcess(NULL,
szCommandLine,
NULL, // Security attribute for process
NULL, // Security attribute for thread
FALSE, // Inheritance - no
0, // Creation flags
NULL, // Environment
NULL, // Current dir
&si,
&procinfo))
{
TRACE((ERROR_MESSAGE,
"Error creating process (szCmdLine=%S), GetLastError=0x%x\n",
szCommandLine, GetLastError()));
procinfo.hProcess = procinfo.hThread = NULL;
rv = ERR_CREATING_PROCESS;
goto exiterr;
}
(*ppCI)->hProcess = procinfo.hProcess;
(*ppCI)->hThread = procinfo.hThread;
(*ppCI)->lProcessId = procinfo.dwProcessId;
(*ppCI)->dwThreadId = procinfo.dwThreadId;
if (wcslen((*ppCI)->pConfigInfo->strDebugger))
// attempt to launch a "debugger"
{
PROCESS_INFORMATION debuggerproc_info;
_snwprintf( szCommandLine,
sizeof(szCommandLine)/sizeof(WCHAR),
(*ppCI)->pConfigInfo->strDebugger,
procinfo.dwProcessId
);
szCommandLine[ sizeof(szCommandLine)/sizeof(WCHAR) - 1 ] = 0;
FillMemory(&si, sizeof(si), 0);
si.cb = sizeof(si);
if (CreateProcess(
NULL,
szCommandLine,
NULL,
NULL,
FALSE,
0,
NULL,
NULL,
&si,
&debuggerproc_info
))
{
TRACE((INFO_MESSAGE, "Debugger is started\n"));
CloseHandle(debuggerproc_info.hProcess);
CloseHandle(debuggerproc_info.hThread);
} else {
TRACE((WARNING_MESSAGE,
"Can't start debugger. GetLastError=%d\n",
GetLastError()));
}
}
rv = Wait4Connect(*ppCI);
if (rv || (*ppCI)->dead)
{
(*ppCI)->dead = TRUE;
TRACE((WARNING_MESSAGE, "Client can't connect\n"));
rv = ERR_CONNECTING;
goto exiterr;
}
hClient = (*ppCI)->hClient;
if ( NULL == hClient )
{
trys = 120; // 2 minutes
do {
hClient = _FindTopWindow((*ppCI)->pConfigInfo->strMainWindowClass,
NULL,
procinfo.dwProcessId);
if (!hClient)
{
Sleep(1000);
trys --;
}
} while(!hClient && trys);
if (!trys)
{
TRACE((WARNING_MESSAGE, "Can't connect"));
rv = ERR_CONNECTING;
goto exiterr;
}
}
// Find the clients child windows
trys = 240; // 2 min
do {
hContainer = _FindWindow(hClient, NULL, NAME_CONTAINERCLASS);
hInput = _FindWindow(hContainer, NULL, NAME_INPUT);
hOutput = _FindWindow(hContainer, NULL, NAME_OUTPUT);
if (!hContainer || !hInput || !hOutput)
{
TRACE((INFO_MESSAGE, "Can't get child windows. Retry"));
Sleep(500);
trys--;
}
} while ((!hContainer || !hInput || !hOutput) && trys);
if (!trys)
{
TRACE((WARNING_MESSAGE, "Can't find child windows"));
rv = ERR_CONNECTING;
goto exiterr;
}
TRACE((INFO_MESSAGE, "hClient = 0x%x\n", hClient));
TRACE((INFO_MESSAGE, "hContainer= 0x%x\n", hContainer));
TRACE((INFO_MESSAGE, "hInput = 0x%x\n", hInput));
TRACE((INFO_MESSAGE, "hOutput = 0x%x\n", hOutput));
(*ppCI)->hClient = hClient;
(*ppCI)->hContainer = hContainer;
(*ppCI)->hInput = hInput;
(*ppCI)->hOutput = hOutput;
#ifdef _RCLX
} else {
// Else what !? This is RCLX mode
// Go in wait mode and wait until some client is connected
// remotely
// set flag in context that this connection works only with remote client
// find the valid server name
while (*lpszServerName && (*lpszServerName) == L'\\')
lpszServerName ++;
TRACE((INFO_MESSAGE,
"A thread in RCLX mode. Wait for some client."
"The target is: %S\n", lpszServerName));
(*ppCI)->dead = FALSE;
(*ppCI)->RClxMode = TRUE;
(*ppCI)->dwThreadId = GetCurrentThreadId();
_AddToClientQ(*ppCI);
rv = _Wait4ConnectTimeout(*ppCI, INFINITE);
if (rv || (*ppCI)->dead)
{
(*ppCI)->dead = TRUE;
TRACE((WARNING_MESSAGE, "Client can't connect to the test controler (us)\n"));
rv = ERR_CONNECTING;
goto exiterr;
} else {
// dwProcessId contains socket. hClient is pointer to RCLX
// context structure, aren't they ?
ASSERT((*ppCI)->lProcessId != INVALID_SOCKET);
ASSERT((*ppCI)->hClient);
TRACE((INFO_MESSAGE, "Client received remote connection\n"));
}
// Next, send connection info to the remote client
// like server to connect to, resolution, etc.
if (!RClx_SendConnectInfo(
(PRCLXCONTEXT)((*ppCI)->hClient),
lpszServerName,
xxRes,
yyRes,
ConnectionFlags))
{
(*ppCI)->dead = TRUE;
TRACE((WARNING_MESSAGE, "Client can't send connection info\n"));
rv = ERR_CONNECTING;
goto exiterr;
}
// Now again wait for connect event
// this time it will be real
rv = Wait4Connect(*ppCI);
if ((*ppCI)->bWillCallAgain)
{
// if so, now the client is disconnected
TRACE((INFO_MESSAGE, "Wait for second call\n"));
(*ppCI)->dead = FALSE;
rv = Wait4Connect(*ppCI);
// Wait for second connect
rv = Wait4Connect(*ppCI);
}
if (rv || (*ppCI)->dead)
{
(*ppCI)->dead = TRUE;
TRACE((WARNING_MESSAGE, "Client(mstsc) can't connect to TS\n"));
rv = ERR_CONNECTING;
goto exiterr;
}
#endif // _RCLX
}
// Save the resolution
(*ppCI)->xRes = xRes;
(*ppCI)->yRes = yRes;
// If username is present
// and no autologon is specified
// try to login
if (wcslen(lpszUserName) && !(*ppCI)->pConfigInfo->Autologon)
{
rv = _Login(*ppCI, lpszServerName, lpszUserName, lpszPassword, lpszDomain);
if (rv)
goto exiterr;
}
exitpt:
return rv;
exiterr:
if (*ppCI)
{
(*ppCI)->bConnectionFailed = TRUE;
if ((szDiscon = SCDisconnect(*ppCI)))
{
TRACE(( WARNING_MESSAGE, "Error disconnecting: %s\n", szDiscon));
}
*ppCI = NULL;
}
return rv;
}
PROTOCOLAPI
LPCSTR
SMCAPI
SCConnect(
LPCWSTR lpszServerName,
LPCWSTR lpszUserName,
LPCWSTR lpszPassword,
LPCWSTR lpszDomain,
IN const int xRes,
IN const int yRes,
PCONNECTINFO *ppCI)
{
INT nConsole;
INT xxRes = xRes;
INT yyRes = yRes;
if ( xRes == -1 && yRes == -1 )
//
// this one goes to the console
//
{
nConsole = TSFLAG_CONSOLE;
xxRes = 0; // there's no change for the console resolution
yyRes = 0;
}
else
nConsole = 0;
return SCConnectEx(
lpszServerName,
lpszUserName,
lpszPassword,
lpszDomain,
NULL, // Default shell (MS Explorer)
xxRes,
yyRes,
g_ConnectionFlags | nConsole, // compression, bmp cache,
// full screen
8, // bpp
0, // audio options
ppCI);
}
/*++
* Function:
* SCDisconnect
* Description:
* Called by smclient, when disconnect command is interpreted
* Arguments:
* pCI - connection context
* Return value:
* Error message. NULL on success
* Called by:
* !smclient
--*/
PROTOCOLAPI
LPCSTR
SMCAPI
SCDisconnect(
PCONNECTINFO pCI)
{
LPCSTR rv = NULL;
INT nCloseTime;
INT nCloseTries = 0;
DWORD dw, dwMaxSearch;
DWORD wres;
HWND hYesNo = NULL;
HWND hDiscBox = NULL;
HWND hDialog = NULL;
BOOL bDiscClosed = FALSE;
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if ( pCI->bConsole )
{
rv = _SCConsDisconnect( pCI );
if ( NULL != pCI->hConsoleExtension )
FreeLibrary( (HMODULE) pCI->hConsoleExtension );
if ( NULL != pCI->pConfigInfo )
{
free( pCI->pConfigInfo );
pCI->pConfigInfo = NULL;
}
free( pCI );
pCI = NULL;
goto exitpt;
}
if ( NULL == pCI->pConfigInfo )
{
//
// no config, no process
//
goto no_config;
}
nCloseTime = pCI->pConfigInfo->WAIT4STR_TIMEOUT;
//
// if we failed at connection time
// search for "Disconnected" dialog
//
//
if ( pCI->bConnectionFailed )
dwMaxSearch = 10;
else
dwMaxSearch = 1;
for( dw = 0; dw < dwMaxSearch; dw++ )
{
hDiscBox =
_FindTopWindow(NULL,
pCI->pConfigInfo->strDisconnectDialogBox,
pCI->lProcessId);
if ( hDiscBox )
{
TRACE(( INFO_MESSAGE, "Closing disconnect dialog( Visible=%d )\n",
IsWindowVisible( hDiscBox )));
PostMessageA(hDiscBox, WM_CLOSE, __LINE__, 0xc001d00d);
bDiscClosed = TRUE;
break;
} else
Sleep( 1000 );
}
if (
#ifdef _RCLX
!(pCI->RClxMode) &&
#endif // _RCLX
NULL != pCI->hProcess )
{
// Try to close the client window
if ( !bDiscClosed )
{
if (
(hDiscBox =
_FindTopWindow(NULL,
pCI->pConfigInfo->strDisconnectDialogBox,
pCI->lProcessId)))
{
PostMessageA(hDiscBox, WM_CLOSE, __LINE__, 0xc001d00d);
}
else
{
pCI->hClient = _FindTopWindow(pCI->pConfigInfo->strMainWindowClass,
NULL,
pCI->lProcessId);
if ( pCI->hClient )
{
TRACE(( INFO_MESSAGE, "Closing main window (Visible=%d)\n",
IsWindowVisible( pCI->hClient )));
PostMessageA(pCI->hClient, WM_CLOSE, __LINE__, 0xc001d00d);
}
}
}
do {
// search for disconnect dialog and close it
if (!hDialog && !hDiscBox &&
(hDiscBox =
_FindTopWindow(NULL,
pCI->pConfigInfo->strDisconnectDialogBox,
pCI->lProcessId)))
PostMessageA(hDiscBox, WM_CLOSE, __LINE__, 0xc001d00d);
/* can't be in startup dialog UI
// if it is in normal dialog close it
if (!hDiscBox && !hDialog &&
(hDialog =
_FindTopWindow(NULL,
pCI->pConfigInfo->strClientCaption,
pCI->lProcessId)))
PostMessageA(hDialog, WM_CLOSE, __LINE__, 0xc001d00d);
*/
// If the client asks whether to close or not
// Answer with 'Yes'
if (!hYesNo)
hYesNo = _FindTopWindow(NULL,
pCI->pConfigInfo->strYesNoShutdown,
pCI->lProcessId);
if (hYesNo)
PostMessageA(hYesNo, WM_KEYDOWN, VK_RETURN, 0);
else if ((nCloseTries % 10) == 5)
{
// On every 10 attempts retry to close the client
if (!pCI->hClient ||
0 != _wcsicmp(pCI->pConfigInfo->strMainWindowClass, NAME_MAINCLASS))
pCI->hClient = _FindTopWindow(pCI->pConfigInfo->strMainWindowClass,
NULL,
pCI->lProcessId);
if (pCI->hClient)
PostMessageA(pCI->hClient, WM_CLOSE, __LINE__, 0xc001d00d);
}
nCloseTries++;
nCloseTime -= 3000;
} while (
(wres = WaitForSingleObject(pCI->hProcess, 3000)) ==
WAIT_TIMEOUT &&
nCloseTime > 0
);
if (wres == WAIT_TIMEOUT)
{
TRACE((WARNING_MESSAGE,
"Can't close process. WaitForSingleObject timeouts\n"));
TRACE((WARNING_MESSAGE,
"Process #%d will be killed\n",
pCI->lProcessId ));
#if 0
{
PROCESS_INFORMATION debuggerproc_info;
STARTUPINFO si;
CHAR szCommandLine[ MAX_STRING_LENGTH ];
_snprintf( szCommandLine,
sizeof( szCommandLine ),
"ntsd -d %d",
pCI->lProcessId
);
FillMemory(&si, sizeof(si), 0);
si.cb = sizeof(si);
if (CreateProcessA(
NULL,
szCommandLine,
NULL,
NULL,
FALSE,
0,
NULL,
NULL,
&si,
&debuggerproc_info
))
{
TRACE((INFO_MESSAGE, "Debugger is started\n"));
CloseHandle(debuggerproc_info.hProcess);
CloseHandle(debuggerproc_info.hThread);
} else {
TRACE((WARNING_MESSAGE,
"Can't start debugger. GetLastError=%d\n",
GetLastError()));
}
}
#else
if (!TerminateProcess(pCI->hProcess, 1))
{
TRACE((WARNING_MESSAGE,
"Can't kill process #%p. GetLastError=%d\n",
pCI->lProcessId, GetLastError()));
}
#endif
}
}
no_config:
if (!_RemoveFromClientQ(pCI))
{
TRACE(( WARNING_MESSAGE,
"Couldn't find CONNECTINFO in the queue\n" ));
}
_CloseConnectInfo(pCI);
exitpt:
return rv;
}
/*++
* Function:
* SCLogoff
* Description:
* Called by smclient, when logoff command is interpreted
* Arguments:
* pCI - connection context
* Return value:
* Error message. NULL on success
* Called by:
* !smclient
--*/
PROTOCOLAPI
LPCSTR
SMCAPI
SCLogoff(
PCONNECTINFO pCI)
{
LPCSTR rv = NULL;
// INT retries = 5;
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (pCI->bConsole)
{
rv = _SCConsLogoff( pCI );
goto exitpt;
}
if (pCI->dead)
{
rv = ERR_CLIENT_IS_DEAD;
goto disconnectpt;
}
/*
do {
// Send Ctrl+Esc
SCSenddata(pCI, WM_KEYDOWN, 17, 1900545);
SCSenddata(pCI, WM_KEYDOWN, 27, 65537);
SCSenddata(pCI, WM_KEYUP, 27, -1073676287);
SCSenddata(pCI, WM_KEYUP, 17, -1071841279);
// Wait for Run... menu
rv = _Wait4Str(pCI,
pCI->pConfigInfo->strStartRun,
pCI->pConfigInfo->WAIT4STR_TIMEOUT/4,
WAIT_STRING);
if (rv)
goto next_retry;
// Send three times Key-Up (scan code 72) and <Enter>
SCSendtextAsMsgs(pCI, pCI->pConfigInfo->strStartLogoff);
rv = _Wait4Str(pCI,
pCI->pConfigInfo->strNTSecurity,
pCI->pConfigInfo->WAIT4STR_TIMEOUT/4,
WAIT_STRING);
next_retry:
retries --;
} while (rv && retries);
if (rv)
goto disconnectpt;
for (retries = 5; retries; retries--) {
SCSendtextAsMsgs(pCI, pCI->pConfigInfo->strNTSecurity_Act);
rv = Wait4Str(pCI, pCI->pConfigInfo->strSureLogoff);
if (!rv) break;
}
*/
rv = SCStart( pCI, L"logoff" );
//
// If SCStart fails, send the magic logoff sequence and hope for the
// best. This is a last resort, and should not normally be needed.
//
if (rv)
{
TRACE((WARNING_MESSAGE,
"Unable to find Run window: blindly trying logoff.\n"));
//
// Clear any pop-up windows with Escape.
//
SCSendtextAsMsgs(pCI, L"\\^\\^\\^");
//
// Send Win+R, or Ctrl+Esc and the Run key, `logoff' and Enter.
// Include a delay to wait for the Run window to appear.
//
_SendRunHotkey(pCI, TRUE);
Sleep(10000);
SCSendtextAsMsgs(pCI, L"logoff\\n");
}
// SCSendtextAsMsgs(pCI, g_strSureLogoffAct); // Press enter
rv = Wait4Disconnect(pCI);
if (rv)
{
TRACE((WARNING_MESSAGE, "Can't close the connection\n"));
}
disconnectpt:
rv = SCDisconnect(pCI);
exitpt:
return rv;
}
/*++
* Function:
* SCStart
* Description:
* Called by smclient, when start command is interpreted
* This functions emulates starting an app from Start->Run menu
* on the server side
* Arguments:
* pCI - connection context
* lpszAppName - command line
* Return value:
* Error message. NULL on success
* Called by:
* !smclient
--*/
PROTOCOLAPI
LPCSTR
SMCAPI
SCStart(
PCONNECTINFO pCI, LPCWSTR lpszAppName)
{
LPCSTR waitres = NULL;
int retries;
// int retries2 = 5;
DWORD dwTimeout;
LPCSTR rv = NULL;
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (pCI->bConsole)
{
rv = _SCConsStart( pCI, lpszAppName );
goto exitpt;
}
if (pCI->dead)
{
rv = ERR_CLIENT_IS_DEAD;
goto exitpt;
}
dwTimeout = 10000; // start the timeout of 10 secs
// Try to start run menu
do {
// Press Ctrl+Esc
for (retries = 0; retries < 5; retries += 1) {
TRACE((ALIVE_MESSAGE, "Start: Sending Ctrl+Esc\n"));
SCSenddata(pCI, WM_KEYDOWN, 17, 1900545);
SCSenddata(pCI, WM_KEYDOWN, 27, 65537);
SCSenddata(pCI, WM_KEYUP, 27, -1073676287);
SCSenddata(pCI, WM_KEYUP, 17, -1071841279);
// If the last wait was unsuccessfull increase the timeout
if (waitres)
dwTimeout += 2000;
// Wait for Run... menu
waitres = _Wait4Str(pCI,
pCI->pConfigInfo->strStartRun,
dwTimeout,
WAIT_STRING);
if (waitres)
{
TRACE((INFO_MESSAGE, "Start: Start menu didn't appear. Retrying\n"));
} else {
TRACE((ALIVE_MESSAGE, "Start: Got the start menu\n"));
break;
}
}
//
// If the Start menu appeared, send the character to open the run
// window.
//
if (!waitres)
{
// sometimes this message is sent before the start menu has the
// input focus therefore let's wait for sometime
Sleep(2000);
TRACE((ALIVE_MESSAGE,
"Start: Sending shortcut 'r' for Run command\n"))
// press 'R' for Run...
SCSendtextAsMsgs(pCI, pCI->pConfigInfo->strStartRun_Act);
}
//
// If the Start menu didn't appear, send the run hotkey (Win+R).
//
else
{
TRACE((WARNING_MESSAGE,
"Start: Start menu didn't appear. Trying hotkey\n"));
_SendRunHotkey(pCI, FALSE);
}
TRACE((ALIVE_MESSAGE, "Start: Waiting for the \"Run\" box\n"));
waitres = _Wait4Str(pCI,
pCI->pConfigInfo->strRunBox,
dwTimeout+10000,
WAIT_STRING);
if (waitres)
// No success, press Esc
{
TRACE((INFO_MESSAGE, "Start: Can't get the \"Run\" box. Retrying\n"));
SCSenddata(pCI, WM_KEYDOWN, 27, 65537);
SCSenddata(pCI, WM_KEYUP, 27, -1073676287);
}
} while (waitres && dwTimeout < pCI->pConfigInfo->WAIT4STR_TIMEOUT);
if (waitres)
{
TRACE((WARNING_MESSAGE, "Start: \"Run\" box didn't appear. Giving up\n"));
rv = ERR_COULDNT_OPEN_PROGRAM;
goto exitpt;
}
TRACE((ALIVE_MESSAGE, "Start: Sending the command line\n"));
// Now we have the focus on the "Run" box, send the app name
rv = SCSendtextAsMsgs(pCI, lpszAppName);
// Hit <Enter>
SCSenddata(pCI, WM_KEYDOWN, 13, 1835009);
SCSenddata(pCI, WM_KEYUP, 13, -1071906815);
exitpt:
return rv;
}
// Eventualy, we are going to change the clipboard
// Syncronize this, so no other thread's AV while
// checking the clipboard content
// store 1 for write, 0 for read
static LONG g_ClipOpened = 0;
/*++
* Function:
* SCClipbaord
* Description:
* Called by smclient, when clipboard command is interpreted
* when eClipOp is COPY_TO_CLIPBOARD it copies the lpszFileName to
* the clipboard. If eClipOp is PASTE_FROM_CLIPBOARD it
* checks the clipboard content against the content of lpszFileName
* Arguments:
* pCI - connection context
* eClipOp - clipboard operation. Possible values:
* COPY_TO_CLIPBOARD and PASTE_FROM_CLIPBOARD
* Return value:
* Error message. NULL on success
* Called by:
* !smclient
--*/
PROTOCOLAPI
LPCSTR
SMCAPI
SCClipboard(
PCONNECTINFO pCI, const CLIPBOARDOPS eClipOp, LPCSTR lpszFileName)
{
LPCSTR rv = NULL;
INT hFile = -1;
LONG clplength = 0;
UINT uiFormat = 0;
PBYTE ghClipData = NULL;
HGLOBAL hNewData = NULL;
PBYTE pClipData = NULL;
BOOL bClipboardOpen = FALSE;
BOOL bFreeClipHandle = TRUE;
LONG prevOp = 1;
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (pCI->bConsole)
{
rv = _SCConsClipboard( pCI, eClipOp, lpszFileName );
goto exitpt;
}
if (pCI->dead)
{
rv = ERR_CLIENT_IS_DEAD;
goto exitpt;
}
if (lpszFileName == NULL || !(*lpszFileName))
{
// No filename specified, work like an empty clipboard is requested
if (eClipOp == COPY_TO_CLIPBOARD)
{
#ifdef _RCLX
if (pCI->RClxMode)
{
if (!RClx_SendClipboard((PRCLXCONTEXT)(pCI->hClient),
NULL, 0, 0))
rv = ERR_COPY_CLIPBOARD;
} else {
#endif // _RCLX
if (!Clp_EmptyClipboard())
rv = ERR_COPY_CLIPBOARD;
#ifdef _RCLX
}
#endif // _RCLX
} else if (eClipOp == PASTE_FROM_CLIPBOARD)
{
#ifdef _RCLX
if (pCI->RClxMode)
{
if (!RClx_SendClipboardRequest((PRCLXCONTEXT)(pCI->hClient), 0))
{
rv = ERR_PASTE_CLIPBOARD;
goto exitpt;
}
if (_Wait4ClipboardTimeout(pCI, pCI->pConfigInfo->WAIT4STR_TIMEOUT))
{
rv = ERR_PASTE_CLIPBOARD;
goto exitpt;
}
// We do not expect to receive clipboard data
// just format ID
if (!pCI->uiClipboardFormat)
// if the format is 0, then there's no clipboard
rv = NULL;
else
rv = ERR_PASTE_CLIPBOARD_DIFFERENT_SIZE;
} else {
#endif // _RCLX
if (Clp_CheckEmptyClipboard())
rv = NULL;
else
rv = ERR_PASTE_CLIPBOARD_DIFFERENT_SIZE;
#ifdef _RCLX
}
#endif // _RCLX
} else {
TRACE((ERROR_MESSAGE, "SCClipboard: Invalid filename\n"));
rv = ERR_INVALID_PARAM;
}
goto exitpt;
}
if (eClipOp == COPY_TO_CLIPBOARD)
{
// Open the file for reading
hFile = _open(lpszFileName, _O_RDONLY|_O_BINARY);
if (hFile == -1)
{
TRACE((ERROR_MESSAGE,
"Error opening file: %s. errno=%d\n", lpszFileName, errno));
rv = ERR_COPY_CLIPBOARD;
goto exitpt;
}
// Get the clipboard length (in the file)
clplength = _filelength(hFile) - sizeof(uiFormat);
// Get the format
if (_read(hFile, &uiFormat, sizeof(uiFormat)) != sizeof(uiFormat))
{
TRACE((ERROR_MESSAGE,
"Error reading from file. errno=%d\n", errno));
rv = ERR_COPY_CLIPBOARD;
goto exitpt;
}
ghClipData = (PBYTE) GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, clplength);
if (!ghClipData)
{
TRACE((ERROR_MESSAGE,
"Can't allocate %d bytes\n", clplength));
rv = ERR_COPY_CLIPBOARD;
goto exitpt;
}
pClipData = (PBYTE) GlobalLock( ghClipData );
if (!pClipData)
{
TRACE((ERROR_MESSAGE,
"Can't lock handle 0x%x\n", ghClipData));
rv = ERR_COPY_CLIPBOARD;
goto exitpt;
}
if (_read(hFile, pClipData, clplength) != clplength)
{
TRACE((ERROR_MESSAGE,
"Error reading from file. errno=%d\n", errno));
rv = ERR_COPY_CLIPBOARD;
goto exitpt;
}
GlobalUnlock(ghClipData);
#ifdef _RCLX
if (pCI->RClxMode)
// RCLX mode, send the data to the client's machine
{
if (!(pClipData = (PBYTE) GlobalLock(ghClipData)))
{
rv = ERR_COPY_CLIPBOARD;
goto exitpt;
}
if (!RClx_SendClipboard((PRCLXCONTEXT)(pCI->hClient),
pClipData, clplength, uiFormat))
{
rv = ERR_COPY_CLIPBOARD;
goto exitpt;
}
} else {
#endif // _RCLX
// Local mode, change the clipboard on this machine
if ((prevOp = InterlockedExchange(&g_ClipOpened, 1)))
{
rv = ERR_CLIPBOARD_LOCKED;
goto exitpt;
}
if (!OpenClipboard(NULL))
{
TRACE((ERROR_MESSAGE,
"Can't open the clipboard. GetLastError=%d\n",
GetLastError()));
rv = ERR_COPY_CLIPBOARD;
goto exitpt;
}
bClipboardOpen = TRUE;
// Empty the clipboard, so we'll have only one entry
EmptyClipboard();
if (!Clp_SetClipboardData(uiFormat, ghClipData, clplength, &bFreeClipHandle))
{
TRACE((ERROR_MESSAGE,
"SetClipboardData failed. GetLastError=%d\n",
GetLastError()));
rv = ERR_COPY_CLIPBOARD;
goto exitpt;
}
#ifdef _RCLX
}
#endif // _RCLX
} else if (eClipOp == PASTE_FROM_CLIPBOARD)
{
INT nClipDataSize;
// Open the file for reading
hFile = _open(lpszFileName, _O_RDONLY|_O_BINARY);
if (hFile == -1)
{
TRACE((ERROR_MESSAGE,
"Error opening file: %s. errno=%d\n", lpszFileName, errno));
rv = ERR_PASTE_CLIPBOARD;
goto exitpt;
}
// Get the clipboard length (in the file)
clplength = _filelength(hFile) - sizeof(uiFormat);
// Get the format
if (_read(hFile, &uiFormat, sizeof(uiFormat)) != sizeof(uiFormat))
{
TRACE((ERROR_MESSAGE,
"Error reading from file. errno=%d\n", errno));
rv = ERR_PASTE_CLIPBOARD;
goto exitpt;
}
//
// TODO: For now, set nClipDataSize to avoid warning, but later
// verify usage is safe.
//
nClipDataSize = 0;
#ifdef _RCLX
// This piece retrieves the clipboard
if (pCI->RClxMode)
// Send request for a clipboard
{
if (!RClx_SendClipboardRequest((PRCLXCONTEXT)(pCI->hClient), uiFormat))
{
rv = ERR_PASTE_CLIPBOARD;
goto exitpt;
}
if (_Wait4ClipboardTimeout(pCI, pCI->pConfigInfo->WAIT4STR_TIMEOUT))
{
rv = ERR_PASTE_CLIPBOARD;
goto exitpt;
}
ghClipData = (PBYTE) pCI->ghClipboard;
// Get the clipboard size
nClipDataSize = pCI->nClipboardSize;
} else {
#endif // _RCLX
// retrieve the local clipboard
if ((prevOp = InterlockedExchange(&g_ClipOpened, 1)))
{
rv = ERR_CLIPBOARD_LOCKED;
goto exitpt;
}
if (!OpenClipboard(NULL))
{
TRACE((ERROR_MESSAGE,
"Can't open the clipboard. GetLastError=%d\n",
GetLastError()));
rv = ERR_PASTE_CLIPBOARD;
goto exitpt;
}
bClipboardOpen = TRUE;
// Retrieve the data
ghClipData = (PBYTE) GetClipboardData(uiFormat);
if (ghClipData)
{
Clp_GetClipboardData(uiFormat,
ghClipData,
&nClipDataSize,
&hNewData);
bFreeClipHandle = FALSE;
}
if (hNewData)
ghClipData = (PBYTE) hNewData;
#ifdef _RCLX
}
#endif // _RCLX
if (!ghClipData)
{
TRACE((ERROR_MESSAGE,
"Can't get clipboard data (empty clipboard ?). GetLastError=%d\n",
GetLastError()));
rv = ERR_PASTE_CLIPBOARD_EMPTY;
goto exitpt;
}
if (!nClipDataSize)
TRACE((WARNING_MESSAGE, "Clipboard is empty.\n"));
pClipData = (PBYTE) GlobalLock(ghClipData);
if (!pClipData)
{
TRACE((ERROR_MESSAGE,
"Can't lock global mem. GetLastError=%d\n",
GetLastError()));
rv = ERR_PASTE_CLIPBOARD;
goto exitpt;
}
#ifdef _RCLX
// Check if the client is on Win16 platform
// and the clipboard is paragraph aligned
// the file size is just bellow this size
if (pCI->RClxMode &&
(strstr(pCI->szClientType, "WIN16") != NULL) &&
((nClipDataSize % 16) == 0) &&
((nClipDataSize - clplength) < 16) &&
(nClipDataSize != 0))
{
// if so, then cut the clipboard size with the difference
nClipDataSize = clplength;
}
else
#endif // _RCLX
if (nClipDataSize != clplength)
{
TRACE((INFO_MESSAGE, "Different length: file=%d, clipbrd=%d\n",
clplength, nClipDataSize));
rv = ERR_PASTE_CLIPBOARD_DIFFERENT_SIZE;
goto exitpt;
}
// compare the data
{
BYTE pBuff[1024];
PBYTE pClp = pClipData;
UINT nBytes;
BOOL bEqu = TRUE;
while (bEqu &&
(nBytes = _read(hFile, pBuff, sizeof(pBuff))) &&
nBytes != -1)
{
if (memcmp(pBuff, pClp, nBytes))
bEqu = FALSE;
pClp += nBytes;
}
if (!bEqu)
{
TRACE((INFO_MESSAGE, "Clipboard and file are not equal\n"));
rv = ERR_PASTE_CLIPBOARD_NOT_EQUAL;
}
}
} else
rv = ERR_UNKNOWN_CLIPBOARD_OP;
exitpt:
// Do the cleanup
// Release the clipboard handle
if (pClipData)
GlobalUnlock(ghClipData);
#ifdef _RCLX
// free any clipboard received in RCLX mode
if (pCI->RClxMode && pCI->ghClipboard)
{
GlobalFree(pCI->ghClipboard);
pCI->ghClipboard = NULL;
}
else
#endif // _RCLX
if (ghClipData && eClipOp == COPY_TO_CLIPBOARD && bFreeClipHandle)
GlobalFree(ghClipData);
if (hNewData)
GlobalFree(hNewData);
// Close the file
if (hFile != -1)
_close(hFile);
// Close the clipboard
if (bClipboardOpen)
CloseClipboard();
if (!prevOp)
InterlockedExchange(&g_ClipOpened, 0);
return rv;
}
/*++
* Function:
* SCSaveClipboard
* Description:
* Save the clipboard in file (szFileName) with
* format specified in szFormatName
* Arguments:
* pCI - connection context
* szFormatName- format name
* szFileName - the name of the file to save to
* Return value:
* Error message. NULL on success
* Called by:
* !perlext
--*/
PROTOCOLAPI
LPCSTR
SMCAPI
SCSaveClipboard(
PCONNECTINFO pCI,
LPCSTR szFormatName,
LPCSTR szFileName)
{
LPCSTR rv = ERR_SAVE_CLIPBOARD;
BOOL bClipboardOpen = FALSE;
UINT nFormatID = 0;
HGLOBAL ghClipData = NULL;
HGLOBAL hNewData = NULL;
INT nClipDataSize;
CHAR *pClipData = NULL;
INT hFile = -1;
LONG prevOp = 1;
// ++++++ First go thru parameter check
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (pCI->dead)
{
rv = ERR_CLIENT_IS_DEAD;
goto exitpt;
}
if (szFormatName == NULL || !(*szFormatName))
{
TRACE((ERROR_MESSAGE, "SCClipboard: Invalid format name\n"));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
if (szFileName == NULL || !(*szFileName))
{
TRACE((ERROR_MESSAGE, "SCClipboard: Invalid filename\n"));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
// ------ End of parameter check
//
#ifdef _RCLX
if (pCI->RClxMode)
{
nFormatID = _GetKnownClipboardFormatIDByName(szFormatName);
if (!nFormatID)
{
TRACE((ERROR_MESSAGE, "Can't get the clipboard format ID: %s.\n", szFormatName));
goto exitpt;
}
// Send request for a clipboard
if (!RClx_SendClipboardRequest((PRCLXCONTEXT)(pCI->hClient), nFormatID))
{
rv = ERR_PASTE_CLIPBOARD;
goto exitpt;
}
if (_Wait4ClipboardTimeout(pCI, pCI->pConfigInfo->WAIT4STR_TIMEOUT))
{
rv = ERR_PASTE_CLIPBOARD;
goto exitpt;
}
ghClipData = pCI->ghClipboard;
// Get the clipboard size
nClipDataSize = pCI->nClipboardSize;
if (!ghClipData || !nClipDataSize)
{
TRACE((WARNING_MESSAGE, "Clipboard is empty.\n"));
goto exitpt;
}
} else {
#endif // _RCLX
// local mode
// Open the clipboard
if ((prevOp = InterlockedExchange(&g_ClipOpened, 1)))
{
rv = ERR_CLIPBOARD_LOCKED;
goto exitpt;
}
if (!OpenClipboard(NULL))
{
TRACE((ERROR_MESSAGE, "Can't open the clipboard. GetLastError=%d\n",
GetLastError()));
goto exitpt;
}
bClipboardOpen = TRUE;
nFormatID = Clp_GetClipboardFormat(szFormatName);
if (!nFormatID)
{
TRACE((ERROR_MESSAGE, "Can't get the clipboard format: %s.\n", szFormatName));
goto exitpt;
}
TRACE((INFO_MESSAGE, "Format ID: %d(0x%X)\n", nFormatID, nFormatID));
// Retrieve the data
ghClipData = GetClipboardData(nFormatID);
if (!ghClipData)
{
TRACE((ERROR_MESSAGE, "Can't get clipboard data. GetLastError=%d\n", GetLastError()));
goto exitpt;
}
Clp_GetClipboardData(nFormatID, ghClipData, &nClipDataSize, &hNewData);
if (hNewData)
ghClipData = hNewData;
if (!nClipDataSize)
{
TRACE((WARNING_MESSAGE, "Clipboard is empty.\n"));
goto exitpt;
}
#ifdef _RCLX
}
#endif // _RCLX
pClipData = (char *) GlobalLock(ghClipData);
if (!pClipData)
{
TRACE((ERROR_MESSAGE, "Can't lock global mem. GetLastError=%d\n", GetLastError()));
goto exitpt;
}
// Open the destination file
hFile = _open(szFileName,
_O_RDWR|_O_CREAT|_O_BINARY|_O_TRUNC,
_S_IREAD|_S_IWRITE);
if (hFile == -1)
{
TRACE((ERROR_MESSAGE, "Can't open a file: %s\n", szFileName));
goto exitpt;
}
// First write the format type
if (_write(hFile, &nFormatID, sizeof(nFormatID)) != sizeof(nFormatID))
{
TRACE((ERROR_MESSAGE, "_write failed. errno=%d\n", errno));
goto exitpt;
}
if (_write(hFile, pClipData, nClipDataSize) != (INT)nClipDataSize)
{
TRACE((ERROR_MESSAGE, "_write failed. errno=%d\n", errno));
goto exitpt;
}
TRACE((INFO_MESSAGE, "File written successfully. %d bytes written\n", nClipDataSize));
rv = NULL;
exitpt:
// Do the cleanup
// Close the file
if (hFile != -1)
_close(hFile);
// Release the clipboard handle
if (pClipData)
GlobalUnlock(ghClipData);
if (hNewData)
GlobalFree(hNewData);
// Close the clipboard
if (bClipboardOpen)
CloseClipboard();
if (!prevOp)
InterlockedExchange(&g_ClipOpened, 0);
#ifdef _RCLX
// free any clipboard received in RCLX mode
if (pCI && pCI->RClxMode && pCI->ghClipboard)
{
GlobalFree(pCI->ghClipboard);
pCI->ghClipboard = NULL;
}
#endif // _RCLX
return rv;
}
/*++
* Function:
* SCSenddata
* Description:
* Called by smclient, when senddata command is interpreted
* Sends an window message to the client
* Arguments:
* pCI - connection context
* uiMessage - the massage Id
* wParam - word param of the message
* lParam - long param of the message
* Return value:
* Error message. NULL on success
* Called by:
* !smclient
--*/
PROTOCOLAPI
LPCSTR
SMCAPI
SCSenddata(
PCONNECTINFO pCI,
const UINT uiMessage,
const WPARAM wParam,
const LPARAM lParam)
{
UINT msg = uiMessage;
LPCSTR rv = NULL;
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (pCI->bConsole)
{
rv = _SCConsSenddata( pCI, uiMessage, wParam, lParam );
goto exitpt;
}
if (pCI->dead)
{
rv = ERR_CLIENT_IS_DEAD;
goto exitpt;
}
// TRACE((ALIVE_MESSAGE, "Senddata: uMsg=%x wParam=%x lParam=%x\n",
// uiMessage, wParam, lParam));
// Determines whether it will
// send the message to local window
// or thru RCLX
#ifdef _RCLX
if (!pCI->RClxMode)
{
#endif // _RCLX
// Obsolete, a client registry setting "Allow Background Input" asserts
// that the client will accept the message
// SetFocus(pCI->hInput);
// SendMessageA(pCI->hInput, WM_SETFOCUS, 0, 0);
SendMessageA(pCI->hInput, msg, wParam, lParam);
#ifdef _RCLX
} else {
// RClxMode
ASSERT(pCI->lProcessId != INVALID_SOCKET);
ASSERT(pCI->hClient);
if (!RClx_SendMessage((PRCLXCONTEXT)(pCI->hClient),
msg, wParam, lParam))
{
TRACE((WARNING_MESSAGE,
"Can't send message thru RCLX\n"));
}
}
#endif // _RCLX
exitpt:
return rv;
}
PROTOCOLAPI
LPCSTR
SMCAPI
SCClientTerminate(PCONNECTINFO pCI)
{
LPCSTR rv = ERR_CLIENTTERMINATE_FAIL;
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
#ifdef _RCLX
if (!(pCI->RClxMode))
{
#endif // _RCLX
if (!TerminateProcess(pCI->hProcess, 1))
{
TRACE((WARNING_MESSAGE,
"Can't kill process #%p. GetLastError=%d\n",
pCI->lProcessId, GetLastError()));
goto exitpt;
}
#ifdef _RCLX
} else {
TRACE((WARNING_MESSAGE,
"ClientTerminate is not supported in RCLX mode yet\n"));
TRACE((WARNING_MESSAGE, "Using disconnect\n"));
}
#endif // _RCLX
rv = SCDisconnect(pCI);
exitpt:
return rv;
}
/*++
* Function:
* SCGetSessionId
* Description:
* Called by smclient, returns the session ID. 0 is invalid, not logged on
* yet
* Arguments:
* pCI - connection context
* Return value:
* session id, 0 is invlid value, -1 is returned on NT4 clients
* Called by:
* !smclient
--*/
PROTOCOLAPI
UINT
SMCAPI
SCGetSessionId(PCONNECTINFO pCI)
{
UINT rv = 0;
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
goto exitpt;
}
if (pCI->dead)
{
goto exitpt;
}
rv = pCI->uiSessionId;
exitpt:
return rv;
}
/*++
* Function:
* SCCheck
* Description:
* Called by smclient, when check command is interpreted
* Arguments:
* pCI - connection context
* lpszCommand - command name
* lpszParam - command parameter
* Return value:
* Error message. NULL on success. Exceptions are GetDisconnectReason and
* GetWait4MultipleStrResult
* Called by:
* !smclient
--*/
PROTOCOLAPI
LPCSTR
SMCAPI
SCCheck(PCONNECTINFO pCI, LPCSTR lpszCommand, LPCWSTR lpszParam)
{
LPCSTR rv = ERR_INVALID_COMMAND;
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (pCI->bConsole)
{
rv = _SCConsCheck( pCI, lpszCommand, lpszParam );
goto exitpt;
}
if (pCI->dead)
{
rv = ERR_CLIENT_IS_DEAD;
goto exitpt;
}
if ( !_stricmp(lpszCommand, "Wait4Str"))
rv = Wait4Str(pCI, lpszParam);
else if (!_stricmp(lpszCommand, "Wait4Disconnect"))
rv = Wait4Disconnect(pCI);
else if (!_stricmp(lpszCommand, "RegisterChat"))
rv = RegisterChat(pCI, lpszParam);
else if (!_stricmp(lpszCommand, "UnregisterChat"))
rv = UnregisterChat(pCI, lpszParam);
else if (!_stricmp(lpszCommand, "GetDisconnectReason"))
rv = GetDisconnectReason(pCI);
else if (!_stricmp(lpszCommand, "Wait4StrTimeout"))
rv = Wait4StrTimeout(pCI, lpszParam);
else if (!_stricmp(lpszCommand, "Wait4MultipleStr"))
rv = Wait4MultipleStr(pCI, lpszParam);
else if (!_stricmp(lpszCommand, "Wait4MultipleStrTimeout"))
rv = Wait4MultipleStrTimeout(pCI, lpszParam);
else if (!_stricmp(lpszCommand, "GetWait4MultipleStrResult"))
rv = GetWait4MultipleStrResult(pCI, lpszParam);
else if (!_stricmp(lpszCommand, "SwitchToProcess"))
rv = SCSwitchToProcess(pCI, lpszParam);
else if (!_stricmp(lpszCommand, "SetClientTopmost"))
rv = SCSetClientTopmost(pCI, lpszParam);
else if (!_strnicmp(lpszCommand, "call:", 5))
rv = SCCallDll(pCI, lpszCommand + 5, lpszParam);
/* **New** */
else if (!_stricmp(lpszCommand, "DoUntil" ))
rv = SCDoUntil( pCI, lpszParam );
exitpt:
return rv;
}
/*
* Extensions and help functions
*/
/*++
* Function:
* Wait4Disconnect
* Description:
* Waits until the client is disconnected
* Arguments:
* pCI - connection context
* Return value:
* Error message. NULL on success
* Called by:
* SCCheck, SCLogoff
--*/
LPCSTR Wait4Disconnect(PCONNECTINFO pCI)
{
WAIT4STRING Wait;
LPCSTR rv = NULL;
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (pCI->dead)
{
rv = ERR_CLIENT_IS_DEAD;
goto exitpt;
}
memset(&Wait, 0, sizeof(Wait));
Wait.evWait = CreateEvent(NULL, //security
TRUE, //manual
FALSE, //initial state
NULL); //name
Wait.lProcessId = pCI->lProcessId;
Wait.pOwner = pCI;
Wait.WaitType = WAIT_DISC;
rv = _WaitSomething(pCI, &Wait, pCI->pConfigInfo->WAIT4STR_TIMEOUT);
if (!rv)
{
TRACE(( INFO_MESSAGE, "Client is disconnected\n"));
}
CloseHandle(Wait.evWait);
exitpt:
return rv;
}
/*++
* Function:
* Wait4Connect
* Description:
* Waits until the client is connect
* Arguments:
* pCI - connection context
* Return value:
* Error message, NULL on success
* Called by:
* SCCOnnect
--*/
LPCSTR Wait4Connect(PCONNECTINFO pCI)
{
return (_Wait4ConnectTimeout(pCI, pCI->pConfigInfo->CONNECT_TIMEOUT));
}
/*++
* Function:
* _Wait4ConnectTimeout
* Description:
* Waits until the client is connect
* Arguments:
* pCI - connection context
* dwTimeout - timeout value
* Return value:
* Error message, NULL on success
* Called by:
* SCConnect
--*/
LPCSTR _Wait4ConnectTimeout(PCONNECTINFO pCI, DWORD dwTimeout)
{
WAIT4STRING Wait;
LPCSTR rv = NULL;
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
memset(&Wait, 0, sizeof(Wait));
Wait.evWait = CreateEvent(NULL, //security
TRUE, //manual
FALSE, //initial state
NULL); //name
Wait.lProcessId = pCI->lProcessId;
Wait.pOwner = pCI;
Wait.WaitType = WAIT_CONN;
rv = _WaitSomething(pCI, &Wait, dwTimeout);
if (!rv)
{
TRACE(( INFO_MESSAGE, "Client is connected\n"));
}
CloseHandle(Wait.evWait);
exitpt:
return rv;
}
/*++
* Function:
* _Wait4ClipboardTimeout
* Description:
* Waits until clipboard response is received from RCLX module
* Arguments:
* pCI - connection context
* dwTimeout - timeout value
* Return value:
* Error message, NULL on success
* Called by:
* SCClipboard
--*/
LPCSTR _Wait4ClipboardTimeout(PCONNECTINFO pCI, DWORD dwTimeout)
{
#ifdef _RCLX
WAIT4STRING Wait;
#endif
LPCSTR rv = NULL;
#ifndef _RCLX
UNREFERENCED_PARAMETER(dwTimeout);
#endif
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
#ifdef _RCLX
if (!(pCI->RClxMode))
#endif // _RCLX
{
TRACE((WARNING_MESSAGE, "WaitForClipboard: Not in RCLX mode\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
#ifdef _RCLX
memset(&Wait, 0, sizeof(Wait));
Wait.evWait = CreateEvent(NULL, //security
TRUE, //manual
FALSE, //initial state
NULL); //name
Wait.lProcessId = pCI->lProcessId;
Wait.pOwner = pCI;
Wait.WaitType = WAIT_CLIPBOARD;
rv = _WaitSomething(pCI, &Wait, dwTimeout);
if (!rv)
{
TRACE(( INFO_MESSAGE, "Clipboard received\n"));
}
CloseHandle(Wait.evWait);
#endif // _RCLX
exitpt:
return rv;
}
/*++
* Function:
* GetDisconnectReason
* Description:
* Retrieves, if possible, the client error box
* Arguments:
* pCI - connection context
* Return value:
* The error box message. NULL if not available
* Called by:
* SCCheck
--*/
LPCSTR GetDisconnectReason(PCONNECTINFO pCI)
{
HWND hDiscBox;
LPCSTR rv = NULL;
HWND hWnd, hwndTop, hwndNext;
CHAR classname[128];
CHAR caption[256];
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (!pCI->dead)
{
rv = ERR_CLIENT_IS_DEAD;
goto exitpt;
}
if (strlen(pCI->szDiscReason))
{
rv = pCI->szDiscReason;
goto exitpt;
}
hDiscBox = _FindTopWindow(NULL, pCI->pConfigInfo->strDisconnectDialogBox, pCI->lProcessId);
if (!hDiscBox)
{
rv = ERR_NORMAL_EXIT;
goto exitpt;
} else {
TRACE((INFO_MESSAGE, "Found hDiscBox=0x%x", hDiscBox));
}
pCI->szDiscReason[0] = 0;
hWnd = NULL;
hwndTop = GetWindow(hDiscBox, GW_CHILD);
if (!hwndTop)
{
TRACE((INFO_MESSAGE, "GetWindow failed. hwnd=0x%x\n", hDiscBox));
goto exitpt;
}
hwndNext = hwndTop;
do {
hWnd = hwndNext;
if (!GetClassNameA(hWnd, classname, sizeof(classname)))
{
TRACE((INFO_MESSAGE, "GetClassName failed. hwnd=0x%x\n", hWnd));
goto nextwindow;
}
if (!GetWindowTextA(hWnd, caption, sizeof(caption)))
{
TRACE((INFO_MESSAGE, "GetWindowText failed. hwnd=0x%x\n"));
goto nextwindow;
}
if (!strcmp(classname, STATIC_CLASS) &&
strlen(classname) <
sizeof(pCI->szDiscReason) - strlen(pCI->szDiscReason) - 3)
{
strcat(pCI->szDiscReason, caption);
strcat(pCI->szDiscReason, "\n");
}
nextwindow:
hwndNext = GetNextWindow(hWnd, GW_HWNDNEXT);
} while (hWnd && hwndNext != hwndTop);
rv = (LPCSTR)pCI->szDiscReason;
exitpt:
return rv;
}
/*++
* Function:
* Wait4Str
* Description:
* Waits for a specific string to come from clients feedback
* Arguments:
* pCI - connection context
* lpszParam - waited string
* Return value:
* Error message, NULL on success
* Called by:
* SCCheck
--*/
LPCSTR Wait4Str(PCONNECTINFO pCI, LPCWSTR lpszParam)
{
return _Wait4Str(pCI, lpszParam, pCI->pConfigInfo->WAIT4STR_TIMEOUT, WAIT_STRING);
}
/*++
* Function:
* Wait4StrTimeout
* Description:
* Waits for a specific string to come from clients feedback
* The timeout is different than default and is specified in
* lpszParam argument, like:
* waited_string<->timeout_value
* Arguments:
* pCI - connection context
* lpszParam - waited string and timeout
* Return value:
* Error message, NULL on success
* Called by:
* SCCheck
--*/
LPCSTR Wait4StrTimeout(PCONNECTINFO pCI, LPCWSTR lpszParam)
{
WCHAR waitstr[MAX_STRING_LENGTH];
WCHAR *sep = wcsstr(lpszParam, CHAT_SEPARATOR);
DWORD dwTimeout;
LPCSTR rv = NULL;
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (pCI->dead)
{
rv = ERR_CLIENT_IS_DEAD;
goto exitpt;
}
if (!sep)
{
TRACE((WARNING_MESSAGE,
"Wait4StrTiemout: No timeout value. Default applying\n"));
rv = Wait4Str(pCI, lpszParam);
} else {
LONG_PTR len = sep - lpszParam;
if (len > sizeof(waitstr) - 1)
len = sizeof(waitstr) - 1;
wcsncpy(waitstr, lpszParam, len);
waitstr[len] = 0;
sep += wcslen(CHAT_SEPARATOR);
dwTimeout = _wtoi(sep);
if (!dwTimeout)
{
TRACE((WARNING_MESSAGE,
"Wait4StrTiemout: No timeout value(%s). Default applying\n",
sep));
dwTimeout = pCI->pConfigInfo->WAIT4STR_TIMEOUT;
}
rv = _Wait4Str(pCI, waitstr, dwTimeout, WAIT_STRING);
}
exitpt:
return rv;
}
/*++
* Function:
* Wait4MultipleStr
* Description:
* Same as Wait4Str, but waits for several strings at once
* the strings are separated by '|' character
* Arguments:
* pCI - connection context
* lpszParam - waited strings
* Return value:
* Error message, NULL on success
* Called by:
* SCCheck
--*/
LPCSTR Wait4MultipleStr(PCONNECTINFO pCI, LPCWSTR lpszParam)
{
return _Wait4Str(pCI, lpszParam, pCI->pConfigInfo->WAIT4STR_TIMEOUT, WAIT_MSTRINGS);
}
/*++
* Function:
* Wait4MultipleStrTimeout
* Description:
* Combination between Wait4StrTimeout and Wait4MultipleStr
* Arguments:
* pCI - connection context
* lpszParam - waited strings and timeout value. Example
* - "string1|string2|...|stringN<->5000"
* Return value:
* Error message, NULL on success
* Called by:
* SCCheck
--*/
LPCSTR Wait4MultipleStrTimeout(PCONNECTINFO pCI, LPCWSTR lpszParam)
{
WCHAR waitstr[MAX_STRING_LENGTH];
WCHAR *sep = wcsstr(lpszParam, CHAT_SEPARATOR);
DWORD dwTimeout;
LPCSTR rv = NULL;
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (pCI->dead)
{
rv = ERR_CLIENT_IS_DEAD;
goto exitpt;
}
pCI->nWait4MultipleStrResult = 0;
pCI->szWait4MultipleStrResult[0] = 0;
if (!sep)
{
TRACE((WARNING_MESSAGE,
"Wait4MultipleStrTiemout: No timeout value. Default applying"));
rv = Wait4MultipleStr(pCI, lpszParam);
} else {
LONG_PTR len = sep - lpszParam;
if (len > sizeof(waitstr) - 1)
len = sizeof(waitstr) - 1;
wcsncpy(waitstr, lpszParam, len);
waitstr[len] = 0;
sep += wcslen(CHAT_SEPARATOR);
dwTimeout = _wtoi(sep);
if (!dwTimeout)
{
TRACE((WARNING_MESSAGE,
"Wait4StrTiemout: No timeout value. Default applying"));
dwTimeout = pCI->pConfigInfo->WAIT4STR_TIMEOUT;
}
rv = _Wait4Str(pCI, waitstr, dwTimeout, WAIT_MSTRINGS);
}
exitpt:
return rv;
}
/*++
* Function:
* GetWait4MultipleStrResult
* Description:
* Retrieves the result from last Wait4MultipleStr call
* Arguments:
* pCI - connection context
* lpszParam - unused
* Return value:
* The string, NULL on error
* Called by:
* SCCheck
--*/
LPCSTR GetWait4MultipleStrResult(PCONNECTINFO pCI, LPCWSTR lpszParam)
{
LPCSTR rv = NULL;
UNREFERENCED_PARAMETER(lpszParam);
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (*pCI->szWait4MultipleStrResult)
rv = (LPCSTR)pCI->szWait4MultipleStrResult;
else
rv = NULL;
exitpt:
return rv;
}
LPCSTR
SMCAPI
SCGetFeedbackStringA(
PCONNECTINFO pCI,
LPSTR szBuff,
UINT maxBuffChars
)
{
LPWSTR szwBuff;
LPCSTR rv;
__try {
szwBuff = (LPWSTR) _alloca(( maxBuffChars ) * sizeof( WCHAR ));
} __except( EXCEPTION_EXECUTE_HANDLER )
{
szwBuff = NULL;
}
if ( NULL == szBuff )
{
rv = ERR_ALLOCATING_MEMORY;
goto exitpt;
}
rv = SCGetFeedbackString( pCI, szwBuff, maxBuffChars );
if ( NULL == rv )
{
WideCharToMultiByte(
CP_UTF8,
0,
szwBuff,
-1,
szBuff,
maxBuffChars,
NULL,
NULL );
}
exitpt:
return rv;
}
LPCSTR
SMCAPI
SCGetFeedbackString(
PCONNECTINFO pCI,
LPWSTR szBuff,
UINT maxBuffChars
)
{
LPCSTR rv = NULL;
INT nFBpos, nFBsize ;
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (pCI->dead)
{
rv = ERR_CLIENT_IS_DEAD;
goto exitpt;
}
if ( NULL == szBuff )
{
TRACE((WARNING_MESSAGE, "SCGetFeedbackString, szBuff is null\n"));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
if (!maxBuffChars)
{
TRACE((WARNING_MESSAGE, "SCGetFeedbackString, maxBuffChars is zero\n"));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
// Grab the buffer pointers
EnterCriticalSection(g_lpcsGuardWaitQueue);
nFBpos = pCI->nFBend + FEEDBACK_SIZE - pCI->nFBsize;
nFBsize = pCI->nFBsize;
LeaveCriticalSection(g_lpcsGuardWaitQueue);
nFBpos %= FEEDBACK_SIZE;
*szBuff = 0;
if (!nFBsize)
// Empty buffer, wait for feedback to receive
{
rv = _Wait4Str(pCI, L"", pCI->pConfigInfo->WAIT4STR_TIMEOUT, WAIT_STRING);
}
if (!rv)
// Pickup from buffer
{
EnterCriticalSection(g_lpcsGuardWaitQueue);
// Adjust the buffer pointers
pCI->nFBsize = pCI->nFBend + FEEDBACK_SIZE - nFBpos - 1;
pCI->nFBsize %= FEEDBACK_SIZE;
_snwprintf( szBuff, maxBuffChars, L"%s", pCI->Feedback[nFBpos] );
LeaveCriticalSection(g_lpcsGuardWaitQueue);
}
exitpt:
return rv;
}
VOID
SMCAPI
SCFreeMem(
PVOID pMem
)
{
if ( NULL != pMem )
free( pMem );
}
/*++
* Function:
* SCGetFeedback
* Description:
* Copies the last received strings to an user buffer
* Arguments:
* pCI - connection context
* pszBufs - pointer to the strings, don't forget to 'SCFreeMem' this buffer
* pnFBCount - number of strings in *pszBuffs
* pnFBMaxStrLen - for now MAX_STRING_LENGTH
* Return value:
* Error message, NULL on success
* Called by:
* * * * EXPORTED * * *
--*/
LPCSTR
SMCAPI
SCGetFeedback(
CONNECTINFO *pCI,
LPWSTR *pszBufs,
UINT *pnFBCount,
UINT *pnFBMaxStrLen
)
{
LPWSTR szBufPtr;
LPWSTR szBufs = NULL;
LPCSTR rv = NULL;
INT nFBpos;
INT nFBindex;
BOOL bCSAcquired = FALSE;
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (pCI->dead)
{
rv = ERR_CLIENT_IS_DEAD;
goto exitpt;
}
if (NULL == pszBufs || NULL == pnFBCount || NULL == pnFBMaxStrLen)
{
TRACE((WARNING_MESSAGE, "SCGetFeedbackStrings, szBufs is null\n"));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
EnterCriticalSection(g_lpcsGuardWaitQueue);
bCSAcquired = TRUE;
if (0 == pCI->nFBsize)
{
TRACE((WARNING_MESSAGE, "No strings available\n"));
rv = ERR_NODATA;
goto exitpt;
}
szBufs = (LPWSTR)malloc(MAX_STRING_LENGTH * pCI->nFBsize * sizeof(WCHAR));
if(!szBufs)
{
TRACE((WARNING_MESSAGE, "Could not allocate buffer array\n"));
rv = ERR_ALLOCATING_MEMORY;
goto exitpt;
}
// prepare the loop
nFBpos = pCI->nFBend;
szBufPtr = szBufs;
nFBindex = 0;
do
{
if(0 == nFBpos)
nFBpos = FEEDBACK_SIZE - 1;
else
nFBpos --;
wcscpy(szBufPtr, pCI->Feedback[nFBpos]);
szBufPtr += MAX_STRING_LENGTH;
//
// loop until we have gathered all the strings
//
nFBindex++;
} while(nFBindex < pCI->nFBsize);
// return back info of strings
*pnFBCount = pCI->nFBsize;
*pnFBMaxStrLen = MAX_STRING_LENGTH;
exitpt:
if ( NULL != rv )
{
if ( NULL != szBufs )
free( szBufs );
szBufs = NULL;
}
if ( bCSAcquired )
LeaveCriticalSection(g_lpcsGuardWaitQueue);
if ( NULL != pszBufs )
*pszBufs = szBufs;
return rv;
}
/*++
* Function:
* SCCallDll
* Description:
* Calls an exported dll function
* Arguments:
* pCI - connection context
* lpszDllExport - dll name and function in form:
* dllname!ExportedFunction
* the function prototype is:
* LPCSTR lpfnFunction( PVOID pCI, LPWCSTR lpszParam )
* lpszParam - parameter passed to the function
* Return value:
* the value returned from the call
* Called by:
* SCCheck
--*/
LPCSTR
SMCAPI
SCCallDll(
PCONNECTINFO pCI,
LPCSTR lpszDllExport,
LPCWSTR lpszParam
)
{
LPCSTR rv = NULL;
PFNSMCDLLIMPORT lpfnImport;
CHAR lpszDllName[ MAX_STRING_LENGTH ];
LPSTR lpszImportName;
LPSTR lpszBang;
HINSTANCE hLib = NULL;
DWORD dwDllNameLen;
if ( NULL == lpszDllExport )
{
TRACE((ERROR_MESSAGE, "SCCallDll: DllExport is NULL\n"));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
if ( NULL == lpszParam )
{
TRACE((ERROR_MESSAGE, "SCCallDll: Param is NULL\n"));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
//
// split the dll and import names
//
lpszBang = strchr( lpszDllExport, '!' );
if ( NULL == lpszBang )
{
TRACE((ERROR_MESSAGE, "SCCallDll: invalid import name (no !)\n"));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
dwDllNameLen = PtrToLong((PVOID)( lpszBang - lpszDllExport ));
if ( 0 == dwDllNameLen )
{
TRACE((ERROR_MESSAGE, "SCCallDll: dll name is empty string\n"));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
// copy the dll name
//
strncpy( lpszDllName, lpszDllExport, dwDllNameLen );
lpszDllName[ dwDllNameLen ] = 0;
// the function name is lpszBang + 1
//
lpszImportName = lpszBang + 1;
TRACE((ALIVE_MESSAGE, "SCCallDll: calling %s!%s(%S)\n",
lpszDllName, lpszImportName, lpszParam ));
hLib = LoadLibraryA( lpszDllName );
if ( NULL == hLib )
{
TRACE((ERROR_MESSAGE, "SCCallDll: can't load %s library: %d\n",
lpszDllName,
GetLastError()));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
lpfnImport = (PFNSMCDLLIMPORT)GetProcAddress( hLib, lpszImportName );
if ( NULL == lpfnImport )
{
TRACE((ERROR_MESSAGE, "SCCallDll: can't get the import proc address "
"of %s. GetLastError=%d\n",
lpszImportName,
GetLastError()));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
__try {
rv = lpfnImport( pCI, lpszParam );
}
__except ( EXCEPTION_EXECUTE_HANDLER )
{
TRACE((ERROR_MESSAGE, "SCCallDll: exception 0x%x\n",
GetExceptionCode()));
rv = ERR_UNKNOWNEXCEPTION;
}
exitpt:
if ( NULL != hLib )
FreeLibrary( hLib );
return rv;
}
/*++
* Function:
* SCDoUntil
* Description:
* Sends keystrokes every 10 seconds until string is received
* Arguments:
* pCI - connection context
* lpszParam - parameter in the form of send_text<->wait_for_this_string
* Return value:
* Error message, NULL on success
* Called by:
* SCCheck
--*/
LPCSTR
SMCAPI
SCDoUntil(
PCONNECTINFO pCI,
LPCWSTR lpszParam
)
{
LPCSTR rv = NULL;
DWORD timeout;
LPWSTR szSendStr, szSepStr, szWaitStr;
DWORD dwlen;
if ( NULL == lpszParam )
{
TRACE((ERROR_MESSAGE, "SCDoUntil: Param is NULL\n"));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
//
// extract the parameters
//
szSepStr = wcsstr( lpszParam, CHAT_SEPARATOR );
if ( NULL == szSepStr )
{
TRACE((ERROR_MESSAGE, "SCDoUntil: missing wait string\n" ));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
szWaitStr = szSepStr + wcslen( CHAT_SEPARATOR );
if ( 0 == szWaitStr[0] )
{
TRACE((ERROR_MESSAGE, "SCDoUntil: wait string is empty\n" ));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
dwlen = ((DWORD)PtrToLong( (PBYTE)(((PBYTE)szSepStr) - ((PBYTE)lpszParam)) )) / sizeof( WCHAR );
__try {
szSendStr = (LPWSTR) _alloca( (dwlen + 1) * sizeof( WCHAR ) );
} __except( EXCEPTION_EXECUTE_HANDLER )
{
szSendStr = NULL;
}
if ( NULL == szSendStr )
{
TRACE((ERROR_MESSAGE, "SCDoUntil: _alloca failed\n" ));
rv = ERR_ALLOCATING_MEMORY;
goto exitpt;
}
wcsncpy( szSendStr, lpszParam, dwlen );
szSendStr[dwlen] = 0;
timeout = 0;
while( timeout < pCI->pConfigInfo->WAIT4STR_TIMEOUT )
{
if ( pCI->dead )
{
rv = ERR_CLIENT_IS_DEAD;
break;
}
SCSendtextAsMsgs( pCI, szSendStr );
rv = _Wait4Str( pCI, szWaitStr, 3000, WAIT_MSTRINGS );
if ( NULL == rv )
break;
timeout += 3000;
}
exitpt:
return rv;
}
#ifdef _RCLX
/*++
* Function:
* _SendRClxData
* Description:
* Sends request for data to the client
* Arguments:
* pCI - connection context
* pRClxData - data to send
* Return value:
* Error message, NULL on success
* Called by:
* SCGetClientScreen
--*/
LPCSTR
_SendRClxData(PCONNECTINFO pCI, PRCLXDATA pRClxData)
{
LPCSTR rv = NULL;
PRCLXCONTEXT pRClxCtx;
RCLXREQPROLOG Request;
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (!(pCI->RClxMode))
{
TRACE((WARNING_MESSAGE, "_SendRClxData: Not in RCLX mode\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
pRClxCtx = (PRCLXCONTEXT)pCI->hClient;
if (!pRClxCtx || pRClxCtx->hSocket == INVALID_SOCKET)
{
TRACE((ERROR_MESSAGE, "Not connected yet, RCLX context is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (!pRClxData)
{
TRACE((ERROR_MESSAGE, "_SendRClxData: Data block is null\n"));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
Request.ReqType = REQ_DATA;
Request.ReqSize = pRClxData->uiSize + sizeof(*pRClxData);
if (!RClx_SendBuffer(pRClxCtx->hSocket, &Request, sizeof(Request)))
{
rv = ERR_CLIENT_DISCONNECTED;
goto exitpt;
}
if (!RClx_SendBuffer(pRClxCtx->hSocket, pRClxData, Request.ReqSize))
{
rv = ERR_CLIENT_DISCONNECTED;
goto exitpt;
}
exitpt:
return rv;
}
#endif // _RCLX
#ifdef _RCLX
/*++
* Function:
* _Wait4RClxData
* Description:
* Waits for data response from RCLX client
* Arguments:
* pCI - connection context
* dwTimeout - timeout value
* Return value:
* Error message, NULL on success
* Called by:
* SCGetClientScreen
--*/
LPCSTR
_Wait4RClxDataTimeout(PCONNECTINFO pCI, DWORD dwTimeout)
{
WAIT4STRING Wait;
LPCSTR rv = NULL;
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (!(pCI->RClxMode))
{
TRACE((WARNING_MESSAGE, "_Wait4RClxData: Not in RCLX mode\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
memset(&Wait, 0, sizeof(Wait));
Wait.evWait = CreateEvent(NULL, //security
TRUE, //manual
FALSE, //initial state
NULL); //name
Wait.lProcessId = pCI->lProcessId;
Wait.pOwner = pCI;
Wait.WaitType = WAIT_DATA;
rv = _WaitSomething(pCI, &Wait, dwTimeout);
if (!rv)
{
TRACE(( INFO_MESSAGE, "RCLX data received\n"));
}
CloseHandle(Wait.evWait);
exitpt:
return rv;
}
#endif // _RCLX
/*++
* Function:
* _Wait4Str
* Description:
* Waits for string(s) with specified timeout
* Arguments:
* pCI - connection context
* lpszParam - waited string(s)
* dwTimeout - timeout value
* WaitType - WAIT_STRING ot WAIT_MSTRING
* Return value:
* Error message, NULL on success
* Called by:
* SCStart, Wait4Str, Wait4StrTimeout, Wait4MultipleStr
* Wait4MultipleStrTimeout, GetFeedbackString
--*/
LPCSTR _Wait4Str(PCONNECTINFO pCI,
LPCWSTR lpszParam,
DWORD dwTimeout,
WAITTYPE WaitType)
{
WAIT4STRING Wait;
INT_PTR parlen;
// int i;
LPCSTR rv = NULL;
ASSERT(pCI);
if (pCI->dead)
{
rv = ERR_CLIENT_IS_DEAD;
goto exitpt;
}
memset(&Wait, 0, sizeof(Wait));
// Check the parameter
parlen = wcslen(lpszParam);
// Copy the string
if (parlen > sizeof(Wait.waitstr)/sizeof(WCHAR)-1)
parlen = sizeof(Wait.waitstr)/sizeof(WCHAR)-1;
wcsncpy(Wait.waitstr, lpszParam, parlen);
Wait.waitstr[parlen] = 0;
Wait.strsize = parlen;
// Convert delimiters to 0s
if (WaitType == WAIT_MSTRINGS)
{
WCHAR *p = Wait.waitstr;
while((p = wcschr(p, WAIT_STR_DELIMITER)))
{
*p = 0;
p++;
}
}
Wait.evWait = CreateEvent(NULL, //security
TRUE, //manual
FALSE, //initial state
NULL); //name
if (!Wait.evWait) {
TRACE((ERROR_MESSAGE, "Couldn't create event\n"));
goto exitpt;
}
Wait.lProcessId = pCI->lProcessId;
Wait.pOwner = pCI;
Wait.WaitType = WaitType;
TRACE(( INFO_MESSAGE, "Expecting string: %S\n", Wait.waitstr));
rv = _WaitSomething(pCI, &Wait, dwTimeout);
TRACE(( INFO_MESSAGE, "String %S received\n", Wait.waitstr));
if (!rv && pCI->dead)
{
rv = ERR_CLIENT_IS_DEAD;
}
CloseHandle(Wait.evWait);
exitpt:
return rv;
}
/*++
* Function:
* _WaitSomething
* Description:
* Wait for some event: string, connect or disconnect
* Meanwhile checks for chat sequences
* Arguments:
* pCI - connection context
* pWait - the event function waits for
* dwTimeout - timeout value
* Return value:
* Error message, NULL on success
* Called by:
* Wait4Connect, Wait4Disconnect, _Wait4Str
--*/
LPCSTR
_WaitSomething(PCONNECTINFO pCI, PWAIT4STRING pWait, DWORD dwTimeout)
{
BOOL bDone = FALSE;
LPCSTR rv = NULL;
DWORD waitres;
ASSERT(pCI || pWait);
_AddToWaitQueue(pCI, pWait);
pCI->evWait4Str = pWait->evWait;
do {
waitres = WaitForMultipleObjects(
pCI->nChatNum+1,
&pCI->evWait4Str,
FALSE,
dwTimeout
);
if ( waitres <= pCI->nChatNum + WAIT_OBJECT_0)
{
if (waitres == WAIT_OBJECT_0)
{
bDone = TRUE;
} else {
PWAIT4STRING pNWait;
ASSERT((unsigned)pCI->nChatNum >= waitres - WAIT_OBJECT_0);
// Here we must send response messages
waitres -= WAIT_OBJECT_0 + 1;
ResetEvent(pCI->aevChatSeq[waitres]);
pNWait = _RetrieveFromWaitQByEvent(pCI->aevChatSeq[waitres]);
ASSERT(pNWait);
ASSERT(wcslen(pNWait->respstr));
TRACE((INFO_MESSAGE,
"Recieved : [%d]%S\n",
pNWait->strsize,
pNWait->waitstr ));
SCSendtextAsMsgs(pCI, (LPCWSTR)pNWait->respstr);
}
} else {
if (*(pWait->waitstr))
{
TRACE((WARNING_MESSAGE,
"Wait for \"%S\" failed: TIMEOUT\n",
pWait->waitstr));
} else {
TRACE((WARNING_MESSAGE, "Wait failed: TIMEOUT\n"));
}
rv = ERR_WAIT_FAIL_TIMEOUT;
bDone = TRUE;
}
} while(!bDone);
pCI->evWait4Str = NULL;
_RemoveFromWaitQueue(pWait);
if (!rv && pCI->dead)
rv = ERR_CLIENT_IS_DEAD;
return rv;
}
/*++
* Function:
* RegisterChat
* Description:
* This regiters a wait4str <-> sendtext pair
* so when we receive a specific string we will send a proper messages
* lpszParam is kind of: XXXXXX<->YYYYYY
* XXXXX is the waited string, YYYYY is the respond
* These command could be nested up to: MAX_WAITING_EVENTS
* Arguments:
* pCI - connection context
* lpszParam - parameter, example:
* "Connect to existing Windows NT session<->\n"
* - hit enter when this string is received
* Return value:
* Error message, NULL on success
* Called by:
* SCCheck, _Login
--*/
LPCSTR RegisterChat(PCONNECTINFO pCI, LPCWSTR lpszParam)
{
PWAIT4STRING pWait;
INT_PTR parlen;
// int i;
INT_PTR resplen;
LPCSTR rv = NULL;
LPCWSTR resp;
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (!lpszParam)
{
TRACE((WARNING_MESSAGE, "Parameter is null\n"));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
if (pCI->dead)
{
rv = ERR_CLIENT_IS_DEAD;
goto exitpt;
}
if (pCI->nChatNum >= MAX_WAITING_EVENTS)
{
TRACE(( WARNING_MESSAGE, "RegisterChat: too much waiting strings\n" ));
goto exitpt;
}
// Split the parameter
resp = wcsstr(lpszParam, CHAT_SEPARATOR);
// Check the strings
if (!resp)
{
TRACE(( WARNING_MESSAGE, "RegisterChat: invalid parameter\n" ));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
parlen = wcslen(lpszParam) - wcslen(resp);
resp += wcslen(CHAT_SEPARATOR);
if (!parlen)
{
TRACE((WARNING_MESSAGE, "RegisterChat empty parameter\n"));
goto exitpt;
}
resplen = wcslen(resp);
if (!resplen)
{
TRACE((WARNING_MESSAGE, "RegisterChat: empty respond string\n" ));
goto exitpt;
}
// Allocate the WAIT4STRING structure
pWait = (PWAIT4STRING)malloc(sizeof(*pWait));
if (!pWait)
{
TRACE((WARNING_MESSAGE,
"RegisterChat: can't allocate %d bytes\n",
sizeof(*pWait) ));
goto exitpt;
}
memset(pWait, 0, sizeof(*pWait));
// Copy the waited string
if (parlen > sizeof(pWait->waitstr)/sizeof(WCHAR)-1)
parlen = sizeof(pWait->waitstr)/sizeof(WCHAR)-1;
wcsncpy(pWait->waitstr, lpszParam, parlen);
pWait->waitstr[parlen] = 0;
pWait->strsize = parlen;
// Copy the respond string
if (resplen > sizeof(pWait->respstr)-1)
resplen = sizeof(pWait->respstr)-1;
wcsncpy(pWait->respstr, resp, resplen);
pWait->respstr[resplen] = 0;
pWait->respsize = resplen;
pWait->evWait = CreateEvent(NULL, //security
TRUE, //manual
FALSE, //initial state
NULL); //name
if (!pWait->evWait) {
TRACE((ERROR_MESSAGE, "Couldn't create event\n"));
free (pWait);
goto exitpt;
}
pWait->lProcessId = pCI->lProcessId;
pWait->pOwner = pCI;
pWait->WaitType = WAIT_STRING;
// _AddToWaitQNoCheck(pCI, pWait);
_AddToWaitQueue(pCI, pWait);
// Add to connection info array
pCI->aevChatSeq[pCI->nChatNum] = pWait->evWait;
pCI->nChatNum++;
exitpt:
return rv;
}
// Remove a WAIT4STRING from waiting Q
// Param is the waited string
/*++
* Function:
* UnregisterChat
* Description:
* Deallocates and removes from waiting Q everithing
* from RegisterChat function
* Arguments:
* pCI - connection context
* lpszParam - waited string
* Return value:
* Error message, NULL on success
* Called by:
* SCCheck, _Login
--*/
LPCSTR UnregisterChat(PCONNECTINFO pCI, LPCWSTR lpszParam)
{
PWAIT4STRING pWait;
LPCSTR rv = NULL;
int i;
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (!lpszParam)
{
TRACE((WARNING_MESSAGE, "Parameter is null\n"));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
pWait = _RemoveFromWaitQIndirect(pCI, lpszParam);
if (!pWait)
{
TRACE((WARNING_MESSAGE,
"UnregisterChat: can't find waiting string: %S\n",
lpszParam ));
goto exitpt;
}
i = 0;
while (i < pCI->nChatNum && pCI->aevChatSeq[i] != pWait->evWait)
i++;
ASSERT(i < pCI->nChatNum);
memmove(pCI->aevChatSeq+i,
pCI->aevChatSeq+i+1,
(pCI->nChatNum-i-1)*sizeof(pCI->aevChatSeq[0]));
pCI->nChatNum--;
CloseHandle(pWait->evWait);
free(pWait);
exitpt:
return rv;
}
/*
* Returns TRUE if the client is dead
*/
PROTOCOLAPI
BOOL
SMCAPI
SCIsDead(PCONNECTINFO pCI)
{
if (!pCI)
return TRUE;
return pCI->dead;
}
/*++
* Function:
* _CloseConnectInfo
* Description:
* Clean all resources for this connection. Close the client
* Arguments:
* pCI - connection context
* Called by:
* SCDisconnect
--*/
VOID
_CloseConnectInfo(PCONNECTINFO pCI)
{
#ifdef _RCLX
PRCLXDATACHAIN pRClxDataChain, pNext;
#endif // _RCLX
ASSERT(pCI);
_FlushFromWaitQ(pCI);
// Close All handles
EnterCriticalSection(g_lpcsGuardWaitQueue);
/* // not needed, the handle is already closed
if (pCI->evWait4Str)
{
CloseHandle(pCI->evWait4Str);
pCI->evWait4Str = NULL;
}
*/
// Chat events are already closed by FlushFromWaitQ
// no need to close them
pCI->nChatNum = 0;
#ifdef _RCLX
if (!pCI->RClxMode)
{
#endif // _RCLX
// The client was local, so we have handles opened
if (pCI->hProcess)
CloseHandle(pCI->hProcess);
if (pCI->hThread)
CloseHandle(pCI->hThread);
pCI->hProcess = pCI->hThread =NULL;
#ifdef _RCLX
} else {
// Hmmm, RCLX mode. Then disconnect the socket
if (pCI->hClient)
RClx_EndRecv((PRCLXCONTEXT)(pCI->hClient));
pCI->hClient = NULL; // Clean the pointer
}
// Clear the clipboard handle (if any)
if (pCI->ghClipboard)
{
GlobalFree(pCI->ghClipboard);
pCI->ghClipboard = NULL;
}
// clear any recevied RCLX data
pRClxDataChain = pCI->pRClxDataChain;
while(pRClxDataChain)
{
pNext = pRClxDataChain->pNext;
free(pRClxDataChain);
pRClxDataChain = pNext;
}
#endif // _RCLX
LeaveCriticalSection(g_lpcsGuardWaitQueue);
if (
#ifdef _RCLX
!pCI->RClxMode &&
#endif // _RCLX
NULL != pCI->pConfigInfo &&
pCI->pConfigInfo->UseRegistry )
{
_DeleteClientRegistry(pCI);
}
if ( NULL != pCI->pConfigInfo )
{
free(pCI->pConfigInfo);
pCI->pConfigInfo = NULL;
}
free(pCI);
pCI = NULL;
}
/*++
* Function:
* _Login
* Description:
* Emulate login procedure
* Arguments:
* pCI - connection context
* lpszServerName
* lpszUserName - user name
* lpszPassword - password
* lpszDomain - domain name
* Return value:
* Error message, NULL on success
* Called by:
* SCConnect
--*/
LPCSTR
_Login(PCONNECTINFO pCI,
LPCWSTR lpszServerName,
LPCWSTR lpszUserName,
LPCWSTR lpszPassword,
LPCWSTR lpszDomain)
{
LPCSTR waitres;
LPCSTR rv = NULL;
WCHAR szBuff[MAX_STRING_LENGTH];
#define _LOGON_RETRYS 5
INT nLogonRetrys = _LOGON_RETRYS;
UINT nLogonWaitTime;
INT nFBSize;
INT nFBEnd;
ASSERT(pCI);
szBuff[MAX_STRING_LENGTH - 1] = 0;
//
// If a smartcard is being used, wait for the smartcard UI, dismiss it,
// then wait for the non-smartcard UI.
//
if (_IsSmartcardActive())
{
waitres = Wait4Str(pCI, pCI->pConfigInfo->strSmartcard);
if (!waitres)
{
SCSendtextAsMsgs(pCI, pCI->pConfigInfo->strSmartcard_Act);
waitres = Wait4Str(pCI, pCI->pConfigInfo->strNoSmartcard);
}
if (waitres)
{
TRACE((WARNING_MESSAGE, "Login failed (smartcard)"));
rv = waitres;
goto exitpt;
}
}
//
// Look for a logon string, which will indicate the current state of the
// logon desktop, and try to get to the logon window.
//
retry_logon:
_snwprintf(szBuff, MAX_STRING_LENGTH - 1, L"%s|%s|%s",
pCI->pConfigInfo->strWinlogon, pCI->pConfigInfo->strPriorWinlogon,
pCI->pConfigInfo->strLogonDisabled);
waitres = Wait4MultipleStr(pCI, szBuff);
if (!waitres)
{
//
// Prior Winlogon: send the string to begin the logon.
//
if (pCI->nWait4MultipleStrResult == 1)
{
SCSendtextAsMsgs(pCI, pCI->pConfigInfo->strPriorWinlogon_Act);
waitres = Wait4Str(pCI, pCI->pConfigInfo->strWinlogon);
}
//
// Dismiss logon-disabled pop-up.
//
else if (pCI->nWait4MultipleStrResult == 2)
{
SCSendtextAsMsgs(pCI, L"\\n");
waitres = Wait4Str(pCI, pCI->pConfigInfo->strWinlogon);
}
}
if (waitres)
{
TRACE((WARNING_MESSAGE, "Login failed"));
rv = waitres;
goto exitpt;
}
ConstructLogonString(
lpszServerName,
lpszUserName,
lpszPassword,
lpszDomain,
szBuff,
MAX_STRING_LENGTH,
pCI->pConfigInfo
);
if ( 0 != szBuff[0] )
{
SCSendtextAsMsgs( pCI, szBuff );
} else {
//
// do the default login
// Hit Alt+U to go to user name field
if ( _LOGON_RETRYS != nLogonRetrys )
{
SCSendtextAsMsgs(pCI, pCI->pConfigInfo->strWinlogon_Act);
SCSendtextAsMsgs(pCI, lpszUserName);
// Hit <Tab> key
Sleep(300);
SCSendtextAsMsgs(pCI, L"\\t");
}
Sleep(700);
SCSendtextAsMsgs(pCI, lpszPassword);
if ( _LOGON_RETRYS != nLogonRetrys )
{
// Hit <Tab> key
Sleep(300);
SCSendtextAsMsgs(pCI, L"\\t");
SCSendtextAsMsgs(pCI, lpszDomain);
Sleep(300);
}
}
// Retry logon in case of
// 1. Winlogon is on background
// 2. Wrong username/password/domain
// 3. Other
// Hit <Enter>
SCSendtextAsMsgs(pCI, L"\\n");
if ( !pCI->pConfigInfo->LoginWait )
goto exitpt;
nLogonWaitTime = 0;
_snwprintf(szBuff, MAX_STRING_LENGTH - 1, L"%s|%s<->1000",
pCI->pConfigInfo->strLogonErrorMessage, pCI->pConfigInfo->strSessionListDlg );
while (!pCI->dead && !pCI->uiSessionId && nLogonWaitTime < pCI->pConfigInfo->CONNECT_TIMEOUT)
{
nFBSize = pCI->nFBsize;
nFBEnd = pCI->nFBend;
//
// check if session list dialog is present
//
if ( pCI->pConfigInfo->strSessionListDlg[0] )
{
waitres = Wait4MultipleStrTimeout(pCI, szBuff);
if (!waitres)
{
TRACE((INFO_MESSAGE, "Session list dialog is present\n" ));
//
// restore buffer
//
pCI->nFBsize = nFBSize;
pCI->nFBend = nFBEnd;
Sleep( 1000 );
SCSendtextAsMsgs(pCI, L"\\n");
Sleep( 1000 );
}
}
// Sleep with wait otherwise the chat won't work
// i.e. this is a hack
waitres = _Wait4Str(pCI, pCI->pConfigInfo->strLogonErrorMessage, 1000, WAIT_STRING);
if (!waitres)
// Error message received
{
//
// restore buffer
//
pCI->nFBsize = nFBSize;
pCI->nFBend = nFBEnd;
Sleep(1000);
SCSendtextAsMsgs(pCI, L"\\n");
Sleep(1000);
break;
}
nLogonWaitTime += 1000;
}
if (!pCI->dead && !pCI->uiSessionId)
{
TRACE((WARNING_MESSAGE, "Logon sequence failed. Retrying (%d)",
nLogonRetrys));
if (nLogonRetrys--)
goto retry_logon;
}
if (!pCI->uiSessionId)
{
// Send Enter, just in case we are not logged yet
SCSendtextAsMsgs(pCI, L"\\n");
rv = ERR_CANTLOGON;
}
exitpt:
return rv;
}
WPARAM _GetVirtualKey(INT scancode)
{
if (scancode == 29) // L Control
return VK_CONTROL;
else if (scancode == 42) // L Shift
return VK_SHIFT;
else if (scancode == 56) // L Alt
return VK_MENU;
else
return MapVirtualKeyA(scancode, 3);
}
/*++
* Function:
* SCSendtextAsMsgs
* Description:
* Converts a string to WM_KEYUP/KEYDOWN messages
* And sends them thru client window
* Arguments:
* pCI - connection context
* lpszString - the string to be send
* it can contain the following escape character:
* \n - Enter, \t - Tab, \^ - Esc, \& - Alt switch up/down
* \XXX - scancode XXX is down, \*XXX - scancode XXX is up
* Return value:
* Error message, NULL on success
* Called by:
* SCLogoff, SCStart, _WaitSomething, _Login
--*/
PROTOCOLAPI
LPCSTR
SMCAPI
SCSendtextAsMsgs(PCONNECTINFO pCI, LPCWSTR lpszString)
{
LPCSTR rv = NULL;
INT scancode = 0;
WPARAM vkKey;
BOOL bShiftDown = FALSE;
BOOL bAltKey = FALSE;
BOOL bCtrlKey = FALSE;
UINT uiMsg;
LPARAM lParam;
DWORD dwShiftDown = (ISWIN9X())?SHIFT_DOWN9X:SHIFT_DOWN;
#define _SEND_KEY(_c_, _m_, _v_, _l_) {/*Sleep(40); */SCSenddata(_c_, _m_, _v_, _l_);}
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (!lpszString)
{
TRACE((ERROR_MESSAGE, "NULL pointer passed to SCSendtextAsMsgs"));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
TRACE(( INFO_MESSAGE, "Sending: \"%S\"\n", lpszString));
/*
// Send KEYUP for the shift(s)
// CapsLock ???!!!
_SEND_KEY(pCI, WM_KEYUP, VK_SHIFT,
WM_KEY_LPARAM(1, 0x2A, 0, 0, 1, 1));
// Ctrl key
_SEND_KEY(pCI, WM_KEYUP, VK_CONTROL,
WM_KEY_LPARAM(1, 0x1D, 0, 0, 1, 1));
// Alt key
_SEND_KEY(pCI, WM_SYSKEYUP, VK_MENU,
WM_KEY_LPARAM(1, 0x38, 0, 0, 1, 1));
*/
for (;*lpszString; lpszString++)
{
if ( pCI->pConfigInfo &&
pCI->pConfigInfo->bUnicode )
{
if ((*lpszString != '\\' && 1 == (rand() & 1) ) || // add randomness and...
*lpszString > 0x80 // send as unicode if non ascii
)
{
//
// send unicode character
//
uiMsg = (!bAltKey || bCtrlKey)?WM_KEYDOWN:WM_SYSKEYDOWN;
_SEND_KEY( pCI, uiMsg, VK_PACKET, (*lpszString << 16) );
uiMsg = (!bAltKey || bCtrlKey)?WM_KEYUP:WM_SYSKEYUP;
_SEND_KEY( pCI, uiMsg, VK_PACKET, (*lpszString << 16) );
continue;
}
}
if (*lpszString != '\\') {
try_again:
if ((scancode = OemKeyScan(*lpszString)) == 0xffffffff)
{
rv = ERR_INVALID_SCANCODE_IN_XLAT;
goto exitpt;
}
// Check the Shift key state
if ((scancode & dwShiftDown) && !bShiftDown)
{
uiMsg = (bAltKey)?WM_SYSKEYDOWN:WM_KEYDOWN;
_SEND_KEY(pCI, uiMsg, VK_SHIFT,
WM_KEY_LPARAM(1, 0x2A, 0, (bAltKey)?1:0, 0, 0));
bShiftDown = TRUE;
}
else if (!(scancode & dwShiftDown) && bShiftDown)
{
uiMsg = (bAltKey)?WM_SYSKEYUP:WM_KEYUP;
_SEND_KEY(pCI, uiMsg, VK_SHIFT,
WM_KEY_LPARAM(1, 0x2A, 0, (bAltKey)?1:0, 1, 1));
bShiftDown = FALSE;
}
} else {
// Non printable symbols
lpszString++;
switch(*lpszString)
{
case 'n': scancode = 0x1C; break; // Enter
case 't': scancode = 0x0F; break; // Tab
case '^': scancode = 0x01; break; // Esc
case 'p': Sleep(100); continue; break; // Sleep for 0.1 sec
case 'P': Sleep(1000); continue; break; // Sleep for 1 sec
case 'x': SCSendMouseClick(pCI, pCI->xRes/2, pCI->yRes/2); continue; break;
case '&':
// Alt key
if (bAltKey)
{
_SEND_KEY(pCI, WM_KEYUP, VK_MENU,
WM_KEY_LPARAM(1, 0x38, 0, 0, 1, 1));
} else {
_SEND_KEY(pCI, WM_SYSKEYDOWN, VK_MENU,
WM_KEY_LPARAM(1, 0x38, 0, 1, 0, 0));
}
bAltKey = !bAltKey;
continue;
case '*':
lpszString ++;
if (isdigit(*lpszString))
{
INT exten;
scancode = _wtoi(lpszString);
TRACE((INFO_MESSAGE, "Scancode: %d UP\n", scancode));
vkKey = _GetVirtualKey(scancode);
uiMsg = (!bAltKey || bCtrlKey)?WM_KEYUP:WM_SYSKEYUP;
if (vkKey == VK_MENU)
bAltKey = FALSE;
else if (vkKey == VK_CONTROL)
bCtrlKey = FALSE;
else if (vkKey == VK_SHIFT)
bShiftDown = FALSE;
exten = (_IsExtendedScanCode(scancode))?1:0;
lParam = WM_KEY_LPARAM(1, scancode, exten, (bAltKey)?1:0, 1, 1);
if (uiMsg == WM_KEYUP)
{
TRACE((INFO_MESSAGE, "WM_KEYUP, 0x%x, 0x%x\n", vkKey, lParam));
} else {
TRACE((INFO_MESSAGE, "WM_SYSKEYUP, 0x%x, 0x%x\n", vkKey, lParam));
}
_SEND_KEY(pCI, uiMsg, vkKey, lParam);
while(isdigit(lpszString[1]))
lpszString++;
} else {
lpszString--;
}
continue;
break;
case 0: continue;
default:
if (isdigit(*lpszString))
{
INT exten;
scancode = _wtoi(lpszString);
TRACE((INFO_MESSAGE, "Scancode: %d DOWN\n", scancode));
vkKey = _GetVirtualKey(scancode);
if (vkKey == VK_MENU)
bAltKey = TRUE;
else if (vkKey == VK_CONTROL)
bCtrlKey = TRUE;
else if (vkKey == VK_SHIFT)
bShiftDown = TRUE;
uiMsg = (!bAltKey || bCtrlKey)?WM_KEYDOWN:WM_SYSKEYDOWN;
exten = (_IsExtendedScanCode(scancode))?1:0;
lParam = WM_KEY_LPARAM(1, scancode, exten, (bAltKey)?1:0, 0, 0);
if (uiMsg == WM_KEYDOWN)
{
TRACE((INFO_MESSAGE, "WM_KEYDOWN, 0x%x, 0x%x\n", vkKey, lParam));
} else {
TRACE((INFO_MESSAGE, "WM_SYSKEYDOWN, 0x%x, 0x%x\n", vkKey, lParam));
}
_SEND_KEY(pCI, uiMsg, vkKey, lParam);
while(isdigit(lpszString[1]))
lpszString++;
continue;
}
goto try_again;
}
}
vkKey = MapVirtualKeyA(scancode, 3);
// Remove flag fields
scancode &= 0xff;
uiMsg = (!bAltKey || bCtrlKey)?WM_KEYDOWN:WM_SYSKEYDOWN;
// Send the scancode
_SEND_KEY(pCI, uiMsg, vkKey,
WM_KEY_LPARAM(1, scancode, 0, (bAltKey)?1:0, 0, 0));
uiMsg = (!bAltKey || bCtrlKey)?WM_KEYUP:WM_SYSKEYUP;
_SEND_KEY(pCI, uiMsg, vkKey,
WM_KEY_LPARAM(1, scancode, 0, (bAltKey)?1:0, 1, 1));
}
// And Alt key
if (bAltKey)
_SEND_KEY(pCI, WM_KEYUP, VK_MENU,
WM_KEY_LPARAM(1, 0x38, 0, 0, 1, 1));
// Shift up
if (bShiftDown)
_SEND_KEY(pCI, WM_KEYUP, VK_LSHIFT,
WM_KEY_LPARAM(1, 0x2A, 0, 0, 1, 1));
// Ctrl key
if (bCtrlKey)
_SEND_KEY(pCI, WM_KEYUP, VK_CONTROL,
WM_KEY_LPARAM(1, 0x1D, 0, 0, 1, 1));
#undef _SEND_KEY
exitpt:
return rv;
}
/*++
* Function:
* SwitchToProcess
* Description:
* Use Alt+Tab to switch to a particular process that is already running
* Arguments:
* pCI - connection context
* lpszParam - the text in the alt-tab box that uniquely identifies the
* process we should stop at (i.e., end up switching to)
* Return value:
* Error message, NULL on success
* Called by:
* SCCheck
--*/
PROTOCOLAPI
LPCSTR
SMCAPI
SCSwitchToProcess(PCONNECTINFO pCI, LPCWSTR lpszParam)
{
#define ALT_TAB_WAIT_TIMEOUT 1000
#define MAX_APPS 20
LPCSTR rv = NULL;
LPCSTR waitres = NULL;
INT retrys = MAX_APPS;
// WCHAR *wszCurrTask = 0;
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (pCI->dead)
{
rv = ERR_CLIENT_IS_DEAD;
goto exitpt;
}
// Wait and look for the string, before we do any switching. This makes
// sure we don't hit the string even before we hit alt-tab, and then
// end up switching to the wrong process
while (_Wait4Str(pCI, lpszParam, ALT_TAB_WAIT_TIMEOUT/5, WAIT_STRING) == 0)
;
// Press alt down
SCSenddata(pCI, WM_KEYDOWN, 18, 540540929);
// Now loop through the list of applications (assuming there is one),
// stopping at our desired app.
do {
SCSenddata(pCI, WM_KEYDOWN, 9, 983041);
SCSenddata(pCI, WM_KEYUP, 9, -1072758783);
waitres = _Wait4Str(pCI, lpszParam, ALT_TAB_WAIT_TIMEOUT, WAIT_STRING);
retrys --;
} while (waitres && retrys);
SCSenddata(pCI, WM_KEYUP, 18, -1070071807);
rv = waitres;
exitpt:
return rv;
}
/*++
* Function:
* SCSetClientTopmost
* Description:
* Swithces the focus to this client
* Arguments:
* pCI - connection context
* lpszParam
* - "0" will remote the WS_EX_TOPMOST style
* - "non_zero" will set it as topmost window
* Return value:
* Error message, NULL on success
* Called by:
* SCCheck
--*/
PROTOCOLAPI
LPCSTR
SMCAPI
SCSetClientTopmost(
PCONNECTINFO pCI,
LPCWSTR lpszParam
)
{
LPCSTR rv = NULL;
BOOL bTop = FALSE;
HWND hClient;
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (pCI->dead)
{
rv = ERR_CLIENT_IS_DEAD;
goto exitpt;
}
#ifdef _RCLX
if (pCI->RClxMode)
{
TRACE((ERROR_MESSAGE, "SetClientOnFocus not supported in RCLX mode\n"));
rv = ERR_NOTSUPPORTED;
goto exitpt;
}
#endif // _RCLX
hClient = _FindTopWindow(pCI->pConfigInfo->strMainWindowClass,
NULL,
pCI->lProcessId);
if (!hClient)
{
TRACE((WARNING_MESSAGE, "Client's window handle is null\n"));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
if (lpszParam)
bTop = (_wtoi(lpszParam) != 0);
else
bTop = 0;
if (!SetWindowPos(hClient,
(bTop)?HWND_TOPMOST:HWND_NOTOPMOST,
0,0,0,0,
SWP_NOMOVE | SWP_NOSIZE))
{
TRACE(( ERROR_MESSAGE, "SetWindowPos failed=%d\n",
GetLastError()));
}
ShowWindow(hClient, SW_SHOWNORMAL);
if (bTop)
{
TRACE((INFO_MESSAGE, "Client is SET as topmost window\n"));
} else {
TRACE((INFO_MESSAGE, "Client is RESET as topmost window\n"));
}
exitpt:
return rv;
}
/*++
* Function:
* _SendMouseClick
* Description:
* Sends a messages for a mouse click
* Arguments:
* pCI - connection context
* xPos - mouse position
* yPos
* Return value:
* error string if fails, NULL on success
* Called by:
* * * * EXPORTED * * *
--*/
PROTOCOLAPI
LPCSTR
SMCAPI
SCSendMouseClick(
PCONNECTINFO pCI,
UINT xPos,
UINT yPos)
{
LPCSTR rv;
rv = SCSenddata(pCI, WM_LBUTTONDOWN, 0, xPos + (yPos << 16));
if (!rv)
SCSenddata(pCI, WM_LBUTTONUP, 0, xPos + (yPos << 16));
return rv;
}
#ifdef _RCLX
/*++
* Function:
* SCSaveClientScreen
* Description:
* Saves in a file rectangle of the client's receive screen buffer
* ( aka shadow bitmap)
* Arguments:
* pCI - connection context
* left, top, right, bottom - rectangle coordinates
* if all == -1 get's the whole screen
* szFileName - file to record
* Return value:
* error string if fails, NULL on success
* Called by:
* * * * EXPORTED * * *
--*/
PROTOCOLAPI
LPCSTR
SMCAPI
SCSaveClientScreen(
PCONNECTINFO pCI,
INT left,
INT top,
INT right,
INT bottom,
LPCSTR szFileName)
{
LPCSTR rv = NULL;
PVOID pDIB = NULL;
UINT uiSize = 0;
if (!szFileName)
{
TRACE((WARNING_MESSAGE, "SCSaveClientScreen: szFileName is NULL\n"));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
// leave the rest of param checking to SCGetClientScreen
rv = SCGetClientScreen(pCI, left, top, right, bottom, &uiSize, &pDIB);
if (rv)
goto exitpt;
if (!pDIB || !uiSize)
{
TRACE((ERROR_MESSAGE, "SCSaveClientScreen: failed, no data\n"));
rv = ERR_NODATA;
goto exitpt;
}
if (!SaveDIB(pDIB, szFileName))
{
TRACE((ERROR_MESSAGE, "SCSaveClientScreen: save failed\n"));
rv = ERR_NODATA;
goto exitpt;
}
exitpt:
if (pDIB)
free(pDIB);
return rv;
}
/*++
* Function:
* SCGetClientScreen
* Description:
* Gets rectangle of the client's receive screen buffer
* ( aka shadow bitmap)
* Arguments:
* pCI - connection context
* left, top, right, bottom - rectangle coordinates
* if all == -1 get's the whole screen
* ppDIB - pointer to the received DIB
* puiSize - size of allocated data in ppDIB
*
* !!!!! DON'T FORGET to free() THAT MEMORY !!!!!
*
* Return value:
* error string if fails, NULL on success
* Called by:
* SCSaveClientScreen
* * * * EXPORTED * * *
--*/
PROTOCOLAPI
LPCSTR
SMCAPI
SCGetClientScreen(
PCONNECTINFO pCI,
INT left,
INT top,
INT right,
INT bottom,
UINT *puiSize,
PVOID *ppDIB)
{
LPCSTR rv;
PRCLXDATA pRClxData;
PREQBITMAP pReqBitmap;
PRCLXDATACHAIN pIter, pPrev, pNext;
PRCLXDATACHAIN pRClxDataChain = NULL;
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (pCI->dead)
{
rv = ERR_CLIENT_IS_DEAD;
goto exitpt;
}
if (!pCI->RClxMode)
{
TRACE((WARNING_MESSAGE, "SCGetClientScreen is not supported in non-RCLX mode\n"));
rv = ERR_NOTSUPPORTED;
goto exitpt;
}
if (!ppDIB || !puiSize)
{
TRACE((WARNING_MESSAGE, "ppDIB and/or puiSize parameter is NULL\n"));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
// Remove all recieved DATA_BITMAP from the recieve buffer
EnterCriticalSection(g_lpcsGuardWaitQueue);
{
pIter = pCI->pRClxDataChain;
pPrev = NULL;
while (pIter)
{
pNext = pIter->pNext;
if (pIter->RClxData.uiType == DATA_BITMAP)
{
// dispose this entry
if (pPrev)
pPrev->pNext = pIter->pNext;
else
pCI->pRClxDataChain = pIter->pNext;
if (!pIter->pNext)
pCI->pRClxLastDataChain = pPrev;
free(pIter);
} else
pPrev = pIter;
pIter = pNext;
}
}
LeaveCriticalSection(g_lpcsGuardWaitQueue);
__try {
pRClxData = (PRCLXDATA) alloca(sizeof(*pRClxData) + sizeof(*pReqBitmap));
} __except (EXCEPTION_EXECUTE_HANDLER)
{
pRClxData = NULL;
}
if ( NULL == pRClxData )
{
goto exitpt;
}
pRClxData->uiType = DATA_BITMAP;
pRClxData->uiSize = sizeof(*pReqBitmap);
pReqBitmap = (PREQBITMAP)pRClxData->Data;
pReqBitmap->left = left;
pReqBitmap->top = top;
pReqBitmap->right = right;
pReqBitmap->bottom = bottom;
TRACE((INFO_MESSAGE, "Getting client's DIB (%d, %d, %d, %d)\n", left, top, right, bottom));
rv = _SendRClxData(pCI, pRClxData);
if (rv)
goto exitpt;
do {
rv = _Wait4RClxDataTimeout(pCI, pCI->pConfigInfo->WAIT4STR_TIMEOUT);
if (rv)
goto exitpt;
if (!pCI->pRClxDataChain)
{
TRACE((ERROR_MESSAGE, "RClxData is not received\n"));
rv = ERR_WAIT_FAIL_TIMEOUT;
goto exitpt;
}
EnterCriticalSection(g_lpcsGuardWaitQueue);
// Get any received DATA_BITMAP
{
pIter = pCI->pRClxDataChain;
pPrev = NULL;
while (pIter)
{
pNext = pIter->pNext;
if (pIter->RClxData.uiType == DATA_BITMAP)
{
// dispose this entry from the chain
if (pPrev)
pPrev->pNext = pIter->pNext;
else
pCI->pRClxDataChain = pIter->pNext;
if (!pIter->pNext)
pCI->pRClxLastDataChain = pPrev;
goto entry_is_found;
} else
pPrev = pIter;
pIter = pNext;
}
entry_is_found:
pRClxDataChain = (pIter && pIter->RClxData.uiType == DATA_BITMAP)?
pIter:NULL;
}
LeaveCriticalSection(g_lpcsGuardWaitQueue);
} while (!pRClxDataChain && !pCI->dead);
if (!pRClxDataChain)
{
TRACE((WARNING_MESSAGE, "SCGetClientScreen: client died\n"));
goto exitpt;
}
*ppDIB = malloc(pRClxDataChain->RClxData.uiSize);
if (!(*ppDIB))
{
TRACE((WARNING_MESSAGE, "Can't allocate %d bytes\n",
pRClxDataChain->RClxData.uiSize));
rv = ERR_ALLOCATING_MEMORY;
goto exitpt;
}
memcpy(*ppDIB,
pRClxDataChain->RClxData.Data,
pRClxDataChain->RClxData.uiSize);
*puiSize = pRClxDataChain->RClxData.uiSize;
exitpt:
if (pRClxDataChain)
free(pRClxDataChain);
return rv;
}
/*++
* Function:
* SCSendVCData
* Description:
* Sends data to a virtual channel
* Arguments:
* pCI - connection context
* szVCName - the virtual channel name
* pData - data
* uiSize - data size
* Return value:
* error string if fails, NULL on success
* Called by:
* * * * EXPORTED * * *
--*/
PROTOCOLAPI
LPCSTR
SMCAPI
SCSendVCData(
PCONNECTINFO pCI,
LPCSTR szVCName,
PVOID pData,
UINT uiSize
)
{
LPCSTR rv;
PRCLXDATA pRClxData = NULL;
CHAR *szName2Send;
PVOID pData2Send;
UINT uiPacketSize;
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (pCI->dead)
{
rv = ERR_CLIENT_IS_DEAD;
goto exitpt;
}
if (!pCI->RClxMode)
{
TRACE((WARNING_MESSAGE, "SCSendVCData is not supported in non-RCLXmode\n"));
rv = ERR_NOTSUPPORTED;
goto exitpt;
}
if (!pData || !uiSize)
{
TRACE((WARNING_MESSAGE, "pData and/or uiSize parameter are NULL\n"));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
if (strlen(szVCName) > MAX_VCNAME_LEN - 1)
{
TRACE((WARNING_MESSAGE, "channel name too long\n"));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
uiPacketSize = sizeof(*pRClxData) + MAX_VCNAME_LEN + uiSize;
pRClxData = (PRCLXDATA) malloc(uiPacketSize);
if (!pRClxData)
{
TRACE((ERROR_MESSAGE, "SCSendVCData: can't allocate %d bytes\n",
uiPacketSize));
rv = ERR_ALLOCATING_MEMORY;
goto exitpt;
}
pRClxData->uiType = DATA_VC;
pRClxData->uiSize = uiPacketSize - sizeof(*pRClxData);
szName2Send = (CHAR *)pRClxData->Data;
strcpy(szName2Send, szVCName);
pData2Send = szName2Send + MAX_VCNAME_LEN;
memcpy(pData2Send, pData, uiSize);
rv = _SendRClxData(pCI, pRClxData);
exitpt:
if (pRClxData)
free(pRClxData);
return rv;
}
/*++
* Function:
* SCRecvVCData
* Description:
* Receives data from virtual channel
* Arguments:
* pCI - connection context
* szVCName - the virtual channel name
* ppData - data pointer
*
* !!!!! DON'T FORGET to free() THAT MEMORY !!!!!
*
* puiSize - pointer to the data size
* Return value:
* error string if fails, NULL on success
* Called by:
* * * * EXPORTED * * *
--*/
PROTOCOLAPI
LPCSTR
SMCAPI
SCRecvVCData(
PCONNECTINFO pCI,
LPCSTR szVCName,
PVOID pData,
UINT uiBlockSize,
UINT *puiBytesRead
)
{
LPCSTR rv;
LPSTR szRecvVCName;
PVOID pChanData;
PRCLXDATACHAIN pIter, pPrev, pNext;
PRCLXDATACHAIN pRClxDataChain = NULL;
UINT uiBytesRead = 0;
BOOL bBlockFree = FALSE;
if (!pCI)
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (pCI->dead)
{
rv = ERR_CLIENT_IS_DEAD;
goto exitpt;
}
if (!pCI->RClxMode)
{
TRACE((WARNING_MESSAGE, "SCRecvVCData is not supported in non-RCLXmode\n"));
rv = ERR_NOTSUPPORTED;
goto exitpt;
}
if (!pData || !uiBlockSize || !puiBytesRead)
{
TRACE((WARNING_MESSAGE, "Invalid parameters\n"));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
if (strlen(szVCName) > MAX_VCNAME_LEN - 1)
{
TRACE((WARNING_MESSAGE, "channel name too long\n"));
rv = ERR_INVALID_PARAM;
goto exitpt;
}
// Extract data entry from this channel
do {
if (!pCI->pRClxDataChain)
{
rv = _Wait4RClxDataTimeout(pCI, pCI->pConfigInfo->WAIT4STR_TIMEOUT);
if (rv)
goto exitpt;
}
EnterCriticalSection(g_lpcsGuardWaitQueue);
// Search for data from this channel
{
pIter = pCI->pRClxDataChain;
pPrev = NULL;
while (pIter)
{
pNext = pIter->pNext;
if (pIter->RClxData.uiType == DATA_VC &&
!_stricmp((LPCSTR) pIter->RClxData.Data, szVCName))
{
if (pIter->RClxData.uiSize - pIter->uiOffset - MAX_VCNAME_LEN <= uiBlockSize)
{
// will read the whole block
// dispose this entry
if (pPrev)
pPrev->pNext = pIter->pNext;
else
pCI->pRClxDataChain = pIter->pNext;
if (!pIter->pNext)
pCI->pRClxLastDataChain = pPrev;
bBlockFree = TRUE;
}
goto entry_is_found;
} else
pPrev = pIter;
pIter = pNext;
}
entry_is_found:
pRClxDataChain = (pIter && pIter->RClxData.uiType == DATA_VC)?
pIter:NULL;
}
LeaveCriticalSection(g_lpcsGuardWaitQueue);
} while (!pRClxDataChain && !pCI->dead);
ASSERT(pRClxDataChain->RClxData.uiType == DATA_VC);
szRecvVCName = (LPSTR) pRClxDataChain->RClxData.Data;
if (_stricmp(szRecvVCName, szVCName))
{
TRACE((ERROR_MESSAGE, "SCRecvVCData: received from different channel: %s\n", szRecvVCName));
ASSERT(0);
}
pChanData = (BYTE *)(pRClxDataChain->RClxData.Data) +
pRClxDataChain->uiOffset + MAX_VCNAME_LEN;
uiBytesRead = pRClxDataChain->RClxData.uiSize -
pRClxDataChain->uiOffset - MAX_VCNAME_LEN;
if (uiBytesRead > uiBlockSize)
uiBytesRead = uiBlockSize;
memcpy(pData, pChanData, uiBytesRead);
pRClxDataChain->uiOffset += uiBytesRead;
rv = NULL;
exitpt:
if (pRClxDataChain && bBlockFree)
{
ASSERT(pRClxDataChain->uiOffset + MAX_VCNAME_LEN == pRClxDataChain->RClxData.uiSize);
free(pRClxDataChain);
}
if (puiBytesRead)
{
*puiBytesRead = uiBytesRead;
TRACE((INFO_MESSAGE, "SCRecvVCData: %d bytes read\n", uiBytesRead));
}
return rv;
}
#endif // _RCLX
/*++
* Function:
* _EnumWindowsProc
* Description:
* Used to find a specific window
* Arguments:
* hWnd - current enumerated window handle
* lParam - pointer to SEARCHWND passed from
* _FindTopWindow
* Return value:
* TRUE on success but window is not found
* FALSE if the window is found
* Called by:
* _FindTopWindow thru EnumWindows
--*/
BOOL CALLBACK _EnumWindowsProc( HWND hWnd, LPARAM lParam )
{
TCHAR classname[128];
TCHAR caption[128];
BOOL rv = TRUE;
DWORD dwProcessId;
LONG_PTR lProcessId;
PSEARCHWND pSearch = (PSEARCHWND)lParam;
if (pSearch->szClassName &&
// !GetClassNameWrp(hWnd, classname, sizeof(classname)/sizeof(classname[0])))
!GetClassNameW(hWnd, classname, sizeof(classname)/sizeof(classname[0])))
{
goto exitpt;
}
if (pSearch->szCaption &&
// !GetWindowTextWrp(hWnd, caption, sizeof(caption)/sizeof(caption[0])))
!GetWindowTextW(hWnd, caption, sizeof(caption)/sizeof(caption[0])))
{
goto exitpt;
}
GetWindowThreadProcessId(hWnd, &dwProcessId);
lProcessId = dwProcessId;
if (
(!pSearch->szClassName || ! // Check for classname
#ifdef UNICODE
wcscmp
#else
strcmp
#endif
(classname, pSearch->szClassName))
&&
(!pSearch->szCaption || !
#ifdef UNICODE
wcscmp
#else
strcmp
#endif
(caption, pSearch->szCaption))
&&
lProcessId == pSearch->lProcessId)
{
((PSEARCHWND)lParam)->hWnd = hWnd;
rv = FALSE;
}
exitpt:
return rv;
}
/*++
* Function:
* _FindTopWindow
* Description:
* Find specific window by classname and/or caption and/or process Id
* Arguments:
* classname - class name to search for, NULL ignore
* caption - caption to search for, NULL ignore
* dwProcessId - process Id, 0 ignore
* Return value:
* window handle found, NULL otherwise
* Called by:
* SCConnect, SCDisconnect, GetDisconnectResult
--*/
HWND _FindTopWindow(LPTSTR classname, LPTSTR caption, LONG_PTR lProcessId)
{
SEARCHWND search;
search.szClassName = classname;
search.szCaption = caption;
search.hWnd = NULL;
search.lProcessId = lProcessId;
EnumWindows(_EnumWindowsProc, (LPARAM)&search);
return search.hWnd;
}
/*++
* Function:
* _FindWindow
* Description:
* Find child window by caption and/or classname
* Arguments:
* hwndParent - the parent window handle
* srchcaption - caption to search for, NULL - ignore
* srchclass - class name to search for, NULL - ignore
* Return value:
* window handle found, NULL otherwise
* Called by:
* SCConnect
--*/
HWND _FindWindow(HWND hwndParent, LPTSTR srchcaption, LPTSTR srchclass)
{
HWND hWnd, hwndTop, hwndNext;
BOOL bFound;
TCHAR classname[128];
TCHAR caption[128];
hWnd = NULL;
hwndTop = GetWindow(hwndParent, GW_CHILD);
if (!hwndTop)
{
TRACE((INFO_MESSAGE, "GetWindow failed. hwnd=0x%x\n", hwndParent));
goto exiterr;
}
bFound = FALSE;
hwndNext = hwndTop;
do {
hWnd = hwndNext;
// if (srchclass && !GetClassNameWrp(hWnd, classname, sizeof(classname)))
if (srchclass && !GetClassNameW(hWnd, classname, sizeof(classname)/sizeof(classname[0])))
{
TRACE((INFO_MESSAGE, "GetClassName failed. hwnd=0x%x\n"));
goto nextwindow;
}
if (srchcaption &&
// !GetWindowTextWrp(hWnd, caption, sizeof(caption)/sizeof(classname[0])))
!GetWindowTextW(hWnd, caption, sizeof(caption)/sizeof(classname[0])/sizeof(classname[0])))
{
TRACE((INFO_MESSAGE, "GetWindowText failed. hwnd=0x%x\n"));
goto nextwindow;
}
if (
(!srchclass || !
#ifdef UNICODE
wcscmp
#else
strcmp
#endif
(classname, srchclass))
&&
(!srchcaption || !
#ifdef UNICODE
wcscmp
#else
strcmp
#endif
(caption, srchcaption))
)
bFound = TRUE;
else {
//
// search recursively
//
HWND hSubWnd = _FindWindow( hWnd, srchcaption, srchclass);
if ( NULL != hSubWnd )
{
bFound = TRUE;
hWnd = hSubWnd;
goto exitpt;
}
}
nextwindow:
hwndNext = GetNextWindow(hWnd, GW_HWNDNEXT);
} while (hWnd && hwndNext != hwndTop && !bFound);
exitpt:
if (!bFound) goto exiterr;
return hWnd;
exiterr:
return NULL;
}
BOOL
_IsExtendedScanCode(INT scancode)
{
static BYTE extscans[] = \
{28, 29, 53, 55, 56, 71, 72, 73, 75, 77, 79, 80, 81, 82, 83, 87, 88};
INT idx;
for (idx = 0; idx < sizeof(extscans); idx++)
{
if (scancode == (INT)extscans[idx])
return TRUE;
}
return FALSE;
}
PROTOCOLAPI
BOOL
SMCAPI
SCOpenClipboard(HWND hwnd)
{
return OpenClipboard(hwnd);
}
PROTOCOLAPI
BOOL
SMCAPI
SCCloseClipboard(VOID)
{
return CloseClipboard();
}
PROTOCOLAPI
LPCSTR
SMCAPI
SCDetach(
PCONNECTINFO pCI
)
{
LPCSTR rv = NULL;
if ( NULL == pCI )
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
rv = ERR_NULL_CONNECTINFO;
goto exitpt;
}
if (!_RemoveFromClientQ(pCI))
{
TRACE(( WARNING_MESSAGE,
"Couldn't find CONNECTINFO in the queue\n" ));
}
_CloseConnectInfo( pCI );
exitpt:
return rv;
}
/*++
* Function:
* SCAttach
* Description:
* Attach CONNECTINFO to a client window, assuming that the client
* is already started
* it uses a special cookie to identify the client in the future
* It is recommended to call SCDetach instead of SCLogoff or SCDisconnect
* Arguments:
* hClient - handle to a container window
* the function will find the client window in the child windows
* lClientCookie - This value is used to identify the client
* in normal SCConnect function the client's process id is used
* here any value could be used, but the client has to be notified for
* it
* ppCI - the function returns non-NULL connection structure on
* success
* Return value:
* SC error message
* Called by:
* exported
--*/
PROTOCOLAPI
LPCSTR
SMCAPI
SCAttach(
HWND hClient,
LONG_PTR lClientCookie,
PCONNECTINFO *ppCI
)
{
LPCSTR rv = NULL;
PCONNECTINFO pCI = NULL;
HWND hContainer = NULL;
HWND hInput = NULL;
HWND hOutput = NULL;
UINT trys;
pCI = (PCONNECTINFO) malloc( sizeof( *pCI ));
if ( NULL == pCI )
{
TRACE(( ERROR_MESSAGE, "SCAttach: failed to allocate memory\n" ));
rv = ERR_ALLOCATING_MEMORY;
goto exitpt;
}
ZeroMemory( pCI, sizeof( *pCI ));
//
// get all the windows we need
//
trys = 240; // 2 min
do {
hContainer = _FindWindow(hClient, NULL, NAME_CONTAINERCLASS);
hInput = _FindWindow(hContainer, NULL, NAME_INPUT);
hOutput = _FindWindow(hContainer, NULL, NAME_OUTPUT);
if (!hContainer || !hInput || !hOutput)
{
TRACE((INFO_MESSAGE, "Can't get child windows. Retry"));
Sleep(500);
trys--;
}
} while ((!hContainer || !hInput || !hOutput) && trys);
if (!trys)
{
TRACE((WARNING_MESSAGE, "Can't find child windows"));
rv = ERR_CONNECTING;
goto exitpt;
}
TRACE((INFO_MESSAGE, "hClient = 0x%x\n", hClient));
TRACE((INFO_MESSAGE, "hContainer= 0x%x\n", hContainer));
TRACE((INFO_MESSAGE, "hInput = 0x%x\n", hInput));
TRACE((INFO_MESSAGE, "hOutput = 0x%x\n", hOutput));
TRACE((INFO_MESSAGE, "ClientCookie= 0x%x\n", lClientCookie ));
pCI->hClient = hClient;
pCI->hContainer = hContainer;
pCI->hInput = hInput;
pCI->hOutput = hOutput;
pCI->lProcessId = lClientCookie;
*ppCI = pCI;
_AddToClientQ(*ppCI);
//
// success !!!
//
exitpt:
if ( NULL != rv && NULL != pCI )
{
SCDetach( pCI );
*ppCI = NULL;
}
return rv;
}
/*++
* Function:
* _IsSmartcardActive
* Description:
* Determine whether or not to look for the smartcard UI.
* Arguments:
* None.
* Return value:
* TRUE if the smartcard UI is expected, FALSE otherwise.
* Called by:
* _Login
* Author:
* Based on code from Sermet Iskin (sermeti) 15-Jan-2002
* Alex Stephens (alexstep) 20-Jan-2002
--*/
BOOL
_IsSmartcardActive(
VOID
)
{
SCARDCONTEXT hCtx;
LPCTSTR mszRdrs;
LPCTSTR szRdr;
DWORD cchRdrs;
DWORD dwRet;
DWORD cRdrs;
SCARD_READERSTATE rgStateArr[MAXIMUM_SMARTCARD_READERS];
DWORD dwIndex;
BOOL fSuccess;
//
// Windows releases earlier than XP (NT 5.1/2600) do not support
// smartcards, so return if running on such.
//
if (!ISSMARTCARDAWARE())
{
TRACE((INFO_MESSAGE, "OS does not support smartcards.\n"));
return FALSE;
}
//
// Load the smartcard library, which will set the appropriate function
// pointers. It is loaded only once, and remains loaded until the process
// exits.
//
dwRet = _LoadSmartcardLibrary();
if (dwRet != ERROR_SUCCESS)
{
TRACE((ERROR_MESSAGE,
"Unable to load smartcard library (error %#x).\n",
dwRet));
return FALSE;
}
ASSERT(g_hSmartcardLibrary != NULL);
//
// Get an scard context. If this fails, the service might not be running.
//
ASSERT(g_pfnSCardEstablishContext != NULL);
dwRet = g_pfnSCardEstablishContext(SCARD_SCOPE_SYSTEM,
NULL,
NULL,
&hCtx);
switch (dwRet)
{
//
// Got scard context.
//
case SCARD_S_SUCCESS:
TRACE((INFO_MESSAGE, "Smartcard context established.\n"));
break;
//
// The smartcard service is not running, so there will be no
// smartcard UI.
//
case SCARD_E_NO_SERVICE:
TRACE((INFO_MESSAGE, "Smartcard service not running.\n"));
return FALSE;
break;
//
// The call has failed.
//
default:
TRACE((ERROR_MESSAGE,
"Unable to establish smartcard context (error %#x).\n",
dwRet));
return FALSE;
break;
}
ASSERT(hCtx != 0);
//
// Always release the smartcard context.
//
fSuccess = FALSE;
try
{
//
// Get the list of the readers, using an auto-allocated buffer.
//
mszRdrs = NULL;
cchRdrs = SCARD_AUTOALLOCATE;
ASSERT(g_pfnSCardListReaders != NULL);
dwRet = g_pfnSCardListReaders(hCtx,
NULL,
(LPTSTR)&mszRdrs,
&cchRdrs);
switch (dwRet)
{
//
// Readers are present.
//
case SCARD_S_SUCCESS:
ASSERT(cchRdrs != 0 &&
mszRdrs != NULL &&
*mszRdrs != TEXT('\0'));
TRACE((INFO_MESSAGE, "Smartcard readers are present.\n"));
break;
//
// No readers are present, so there will be no smartcard UI.
//
case SCARD_E_NO_READERS_AVAILABLE:
TRACE((INFO_MESSAGE, "No smartcard readers are present.\n"));
leave;
break;
//
// The call has failed.
//
default:
TRACE((ERROR_MESSAGE,
"Unable to get smartcard-reader list (error %#x).\n",
dwRet));
leave;
break;
}
//
// Always free the reader-list buffer, which is allocated by the
// smartcard code.
//
try
{
//
// Count the number of readers.
//
ZeroMemory(rgStateArr, sizeof(rgStateArr));
for (szRdr = _FirstString(mszRdrs), cRdrs = 0;
szRdr != NULL;
szRdr = _NextString(szRdr))
{
rgStateArr[cRdrs].szReader = szRdr;
rgStateArr[cRdrs].dwCurrentState = SCARD_STATE_UNAWARE;
cRdrs += 1;
}
//
// Query for reader states.
//
ASSERT(g_pfnSCardGetStatusChange != NULL);
dwRet = g_pfnSCardGetStatusChange(hCtx, 0, rgStateArr, cRdrs);
if (dwRet != SCARD_S_SUCCESS)
{
TRACE((
ERROR_MESSAGE,
"Unable to query smartcard-reader states (error %#x).\n",
dwRet));
leave;
}
//
// Check each reader for a card. If one is found, the smartcard
// UI must be handled.
//
for (dwIndex = 0; dwIndex < cRdrs; dwIndex += 1)
{
if (rgStateArr[dwIndex].dwEventState & SCARD_STATE_PRESENT)
{
TRACE((INFO_MESSAGE, "Smartcard present.\n"));
fSuccess = TRUE;
leave;
}
}
//
// No smartcards were found, so there will be no smartcard UI.
//
TRACE((INFO_MESSAGE, "No smartcards are present.\n"));
}
//
// Free reader strings.
//
finally
{
ASSERT(g_pfnSCardFreeMemory != NULL);
ASSERT(hCtx != 0);
ASSERT(mszRdrs != NULL);
g_pfnSCardFreeMemory(hCtx, mszRdrs);
mszRdrs = NULL;
}
}
//
// Release smartcard context.
//
finally
{
ASSERT(g_pfnSCardReleaseContext != NULL);
ASSERT(hCtx != 0);
g_pfnSCardReleaseContext(hCtx);
hCtx = 0;
}
return fSuccess;
}
/*++
* Function:
* _LoadSmartcardLibrary
* Description:
* This routine loads the smartcard library.
* Arguments:
* None.
* Return value:
* ERROR_SUCCESS if successful, an appropriate Win32 error code
* otherwise.
* Called by:
* _IsSmartcardActive
* Author:
* Alex Stephens (alexstep) 20-Jan-2002
--*/
DWORD
_LoadSmartcardLibrary(
VOID
)
{
HANDLE hSmartcardLibrary;
HANDLE hPreviousSmartcardLibrary;
//
// If the smartcard library has already been loaded, succeed.
//
if (g_hSmartcardLibrary != NULL &&
g_pfnSCardEstablishContext != NULL &&
g_pfnSCardListReaders != NULL &&
g_pfnSCardGetStatusChange != NULL &&
g_pfnSCardFreeMemory != NULL &&
g_pfnSCardReleaseContext != NULL)
{
return ERROR_SUCCESS;
}
//
// Load the library.
//
hSmartcardLibrary = LoadLibrary(SMARTCARD_LIBRARY);
if (hSmartcardLibrary == NULL)
{
TRACE((ERROR_MESSAGE, "Unable to load smardcard library.\n"));
return GetLastError();
}
//
// Save the library handle to the global pointer. If it has already been
// set, decrement the reference count.
//
hPreviousSmartcardLibrary =
InterlockedExchangePointer(&g_hSmartcardLibrary,
hSmartcardLibrary);
if (hPreviousSmartcardLibrary != NULL)
{
RTL_VERIFY(FreeLibrary(hSmartcardLibrary));
}
//
// Get the addresses of the smartcard routines.
//
return _GetSmartcardRoutines();
}
/*++
* Function:
* _GetSmartcardRoutines
* Description:
* This routine sets the global function pointers used to call the
* smartcard routines.
* Arguments:
* None.
* Return value:
* ERROR_SUCCESS if successful, an appropriate Win32 error code
* otherwise.
* Called by:
* _LoadSmartcardLibrary
* Author:
* Alex Stephens (alexstep) 20-Jan-2002
--*/
DWORD
_GetSmartcardRoutines(
VOID
)
{
FARPROC pfnSCardEstablishContext;
FARPROC pfnSCardListReaders;
FARPROC pfnSCardGetStatusChange;
FARPROC pfnSCardFreeMemory;
FARPROC pfnSCardReleaseContext;
//
// If the smartcard pointers have already been set, succeed.
//
ASSERT(g_hSmartcardLibrary != NULL);
if (g_pfnSCardEstablishContext != NULL &&
g_pfnSCardListReaders != NULL &&
g_pfnSCardGetStatusChange != NULL &&
g_pfnSCardFreeMemory != NULL &&
g_pfnSCardReleaseContext != NULL)
{
return ERROR_SUCCESS;
}
//
// Get the address of each routine.
//
pfnSCardEstablishContext = GetProcAddress(g_hSmartcardLibrary,
SCARDESTABLISHCONTEXT);
if (pfnSCardEstablishContext == NULL)
{
TRACE((ERROR_MESSAGE,
"Unable to get SCardEstablishContext address.\n"));
return GetLastError();
}
pfnSCardListReaders = GetProcAddress(g_hSmartcardLibrary,
SCARDLISTREADERS);
if (pfnSCardListReaders == NULL)
{
TRACE((ERROR_MESSAGE, "Unable to get SCardListReaders address.\n"));
return GetLastError();
}
pfnSCardGetStatusChange = GetProcAddress(g_hSmartcardLibrary,
SCARDGETSTATUSCHANGE);
if (pfnSCardGetStatusChange == NULL)
{
TRACE((ERROR_MESSAGE,
"Unable to get SCardGetStatusChange address.\n"));
return GetLastError();
}
pfnSCardFreeMemory = GetProcAddress(g_hSmartcardLibrary,
SCARDFREEMEMORY);
if (pfnSCardFreeMemory == NULL)
{
TRACE((ERROR_MESSAGE, "Unable to get SCardFreeMemory address.\n"));
return GetLastError();
}
pfnSCardReleaseContext = GetProcAddress(g_hSmartcardLibrary,
SCARDRELEASECONTEXT);
if (pfnSCardReleaseContext == NULL)
{
TRACE((ERROR_MESSAGE,
"Unable to get SCardReleaseContext address.\n"));
return GetLastError();
}
//
// Fill in any the global pointers. It would be better to
// compare/exchange, but Windows 95 lacks the necessary APIs.
//
InterlockedExchangePointer((PVOID *)&g_pfnSCardEstablishContext,
pfnSCardEstablishContext);
ASSERT(g_pfnSCardEstablishContext != NULL);
InterlockedExchangePointer((PVOID *)&g_pfnSCardListReaders,
pfnSCardListReaders);
ASSERT(g_pfnSCardListReaders != NULL);
InterlockedExchangePointer((PVOID *)&g_pfnSCardGetStatusChange,
pfnSCardGetStatusChange);
ASSERT(g_pfnSCardGetStatusChange != NULL);
InterlockedExchangePointer((PVOID *)&g_pfnSCardFreeMemory,
pfnSCardFreeMemory);
ASSERT(g_pfnSCardFreeMemory != NULL);
InterlockedExchangePointer((PVOID *)&g_pfnSCardReleaseContext,
pfnSCardReleaseContext);
ASSERT(g_pfnSCardReleaseContext != NULL);
return ERROR_SUCCESS;
}
/*++
* Function:
* _FirstString
* Description:
* This routine returns a pointer to the first string in a multistring,
* or NULL if there aren't any.
* Arguments:
* szMultiString - This supplies the address of the current position
* within a Multi-string structure.
* Return value:
* The address of the first null-terminated string in the structure, or
* NULL if there are no strings.
* Called by:
* _IsSmartcardActive
* Author:
* Doug Barlow (dbarlow) 11/25/1996
* Alex Stephens (alexstep) 20-Jan-2002
--*/
LPCTSTR
_FirstString(
IN LPCTSTR szMultiString
)
{
//
// If the multi-string is NULL, or is empty, there is no first string.
//
if (szMultiString == NULL || *szMultiString == TEXT('\0'))
{
return NULL;
}
return szMultiString;
}
/*++
* Function:
* _NextString
* Description:
* In some cases, the Smartcard API returns multiple strings, separated
* by Null characters, and terminated by two null characters in a row.
* This routine simplifies access to such structures. Given the current
* string in a multi-string structure, it returns the next string, or
* NULL if no other strings follow the current string.
* Arguments:
* szMultiString - This supplies the address of the current position
* within a Multi-string structure.
* Return value:
* The address of the next Null-terminated string in the structure, or
* NULL if no more strings follow.
* Called by:
* _IsSmartcardActive
* Author:
* Doug Barlow (dbarlow) 8/12/1996
* Alex Stephens (alexstep) 20-Jan-2002
--*/
LPCTSTR
_NextString(
IN LPCTSTR szMultiString
)
{
DWORD_PTR dwLength;
LPCTSTR szNext;
//
// If the multi-string is NULL, or is empty, there is no next string.
//
if (szMultiString == NULL || *szMultiString == TEXT('\0'))
{
return NULL;
}
//
// Get the length of the current string.
//
dwLength = _tcslen(szMultiString);
ASSERT(dwLength != 0);
//
// Skip the current string, including the terminating NULL, and check to
// see if there is a next string.
//
szNext = szMultiString + dwLength + 1;
if (*szNext == TEXT('\0'))
{
return NULL;
}
return szNext;
}
/*++
* Function:
* _SendRunHotkey
* Description:
* This routine sends the Windows hotkey used to open the shell's Run
* window.
* Note: Keyboard hooks must be enabled for this to work!
* Arguments:
* pCI - Supplies the connection context.
* bFallBack - Supplies a value indicating whether or not to fall back
* to Ctrl+Esc and R if the keyboard hook is disabled.
* Return value:
* None.
* Called by:
* SCLogoff
* SCStart
* Author:
* Alex Stephens (alexstep) 15-Jan-2002
--*/
VOID
_SendRunHotkey(
IN CONST PCONNECTINFO pCI,
IN BOOL bFallBack
)
{
ASSERT(pCI != NULL);
//
// Send Win+R if the keyboard hook is enabled.
//
if (pCI->pConfigInfo->KeyboardHook == TCLIENT_KEYBOARD_HOOK_ALWAYS)
{
TRACE((INFO_MESSAGE, "Sending Win+R hotkey.\n"));
SCSenddata(pCI, WM_KEYDOWN, 0x0000005B, 0x015B0001);
SCSenddata(pCI, WM_KEYDOWN, 0x00000052, 0x00130001);
SCSenddata(pCI, WM_CHAR, 0x00000072, 0x00130001);
SCSenddata(pCI, WM_KEYUP, 0x00000052, 0x80130001);
SCSenddata(pCI, WM_KEYUP, 0x0000005B, 0x815B0001);
}
//
// If the keyboard hook is not enabled, either fail or try Ctrl+Esc and
// the Run key.
//
else
{
if (bFallBack)
{
TRACE((INFO_MESSAGE, "Sending Ctrl+Esc and Run key.\n"));
SCSenddata(pCI, WM_KEYDOWN, 0x00000011, 0x001D0001);
SCSenddata(pCI, WM_KEYDOWN, 0x0000001B, 0x00010001);
SCSenddata(pCI, WM_KEYUP, 0x0000001B, 0xC0010001);
SCSenddata(pCI, WM_KEYUP, 0x00000011, 0xC01D0001);
SCSendtextAsMsgs(pCI, pCI->pConfigInfo->strStartRun_Act);
}
else
{
TRACE((WARNING_MESSAGE,
"Keyboard hook disabled! Cannot send Win+R!\n"));
}
}
}
/*++
* Function:
* SCClientHandle
* Description:
* Get client window
* Arguments:
* pCI - connection context
* Return value:
* window handle found, NULL otherwise
* Called by:
*
--*/
PROTOCOLAPI
HWND
SMCAPI
SCGetClientWindowHandle(
PCONNECTINFO pCI
)
{
HWND hWnd;
hWnd = NULL;
if ( NULL == pCI )
{
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
goto exitpt;
}
hWnd = pCI->hClient;
if (!hWnd)
{
TRACE((ERROR_MESSAGE, "SCGetClientHandle failed\n"));
goto exitpt;
}
exitpt:
return hWnd;
}