/*++ * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #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 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 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 key Sleep(300); SCSendtextAsMsgs(pCI, L"\\t"); } Sleep(700); SCSendtextAsMsgs(pCI, lpszPassword); if ( _LOGON_RETRYS != nLogonRetrys ) { // Hit 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 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; }