Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5629 lines
147 KiB

  1. /*++
  2. * File name:
  3. * scfuncs.cpp
  4. * Contents:
  5. * Functions exported to smclient intepreter
  6. *
  7. * Copyright (C) 1998-1999 Microsoft Corp.
  8. --*/
  9. #pragma warning(disable:4152) // nonstandard extension, function/data pointer
  10. // conversion in expression
  11. #pragma warning(disable:4201) // nonstandard extension used : nameless
  12. // struct/union
  13. #pragma warning(disable:4706) // assignment within conditional expression
  14. #include <windows.h>
  15. #include <stdio.h>
  16. #include <malloc.h>
  17. #include <process.h>
  18. #include <string.h>
  19. #include <stdlib.h>
  20. #include <ctype.h>
  21. #include <stdlib.h>
  22. #include <io.h>
  23. #include <fcntl.h>
  24. #include <sys/stat.h>
  25. #include <tchar.h>
  26. #include <winscard.h>
  27. #include <winsock.h>
  28. #include "tclient.h"
  29. #define PROTOCOLAPI // __declspec(dllexport)
  30. #include "protocol.h"
  31. #include "extraexp.h"
  32. #include "gdata.h"
  33. #include "queues.h"
  34. #include "misc.h"
  35. #ifdef _RCLX
  36. #include "rclx.h"
  37. #endif // _RCLX
  38. #include "sccons.h"
  39. #include "scfuncs.h"
  40. #define TSFLAG_CONSOLE 8
  41. // This structure is used by _FindTopWindow
  42. typedef struct _SEARCHWND {
  43. TCHAR *szClassName; // The class name of searched window,
  44. // NULL - ignore
  45. TCHAR *szCaption; // Window caption, NULL - ignore
  46. LONG_PTR lProcessId; // Process Id of the owner, 0 - ignore
  47. HWND hWnd; // Found window handle
  48. } SEARCHWND, *PSEARCHWND;
  49. //
  50. // type of imported functions, see check("call...") statement
  51. //
  52. typedef LPCSTR (_cdecl *PFNSMCDLLIMPORT)( PVOID, LPCWSTR );
  53. //
  54. // Function pointers for smarcard APIs. These are not supported on versions
  55. // of Windows before Windows 95 OSR2 or Windows NT 4.0 SP3, so are loaded at
  56. // runtime.
  57. //
  58. #define SMARTCARD_LIBRARY TEXT("winscard.dll")
  59. HMODULE g_hSmartcardLibrary;
  60. #define SCARDESTABLISHCONTEXT "SCardEstablishContext"
  61. LONG
  62. (WINAPI
  63. *g_pfnSCardEstablishContext)(
  64. DWORD,
  65. LPCVOID,
  66. LPCVOID,
  67. LPSCARDCONTEXT
  68. );
  69. #ifdef UNICODE
  70. #define SCARDLISTREADERS "SCardListReadersW"
  71. #else
  72. #define SCARDLISTREADERS "SCardListReadersA"
  73. #endif
  74. LONG
  75. (WINAPI
  76. *g_pfnSCardListReaders)(
  77. SCARDCONTEXT,
  78. LPCTSTR,
  79. LPTSTR,
  80. LPDWORD
  81. );
  82. #ifdef UNICODE
  83. #define SCARDGETSTATUSCHANGE "SCardGetStatusChangeW"
  84. #else
  85. #define SCARDGETSTATUSCHANGE "SCardGetStatusChangeA"
  86. #endif
  87. LONG
  88. (WINAPI
  89. *g_pfnSCardGetStatusChange)(
  90. SCARDCONTEXT,
  91. DWORD,
  92. LPSCARD_READERSTATE,
  93. DWORD
  94. );
  95. #define SCARDFREEMEMORY "SCardFreeMemory"
  96. LONG
  97. (WINAPI
  98. *g_pfnSCardFreeMemory)(
  99. SCARDCONTEXT,
  100. LPCVOID
  101. );
  102. #define SCARDRELEASECONTEXT "SCardReleaseContext"
  103. LONG
  104. (WINAPI
  105. *g_pfnSCardReleaseContext)(
  106. IN SCARDCONTEXT
  107. );
  108. /*++
  109. * Function:
  110. * SCInit
  111. * Description:
  112. * Called by smclient after the library is loaded.
  113. * Passes trace routine
  114. * Arguments:
  115. * pInitData - contains a trace routine
  116. * Called by:
  117. * !smclient
  118. --*/
  119. PROTOCOLAPI
  120. VOID
  121. SMCAPI
  122. SCInit(SCINITDATA *pInitData)
  123. {
  124. g_pfnPrintMessage = pInitData->pfnPrintMessage;
  125. }
  126. /*++
  127. * Function:
  128. * SCConnectEx
  129. * Description:
  130. * Called by smclient when connect command is interpreted
  131. * Arguments:
  132. * lpszServerName - server to connect to
  133. * lpszUserName - login user name. Empty string means no login
  134. * lpszPassword - login password
  135. * lpszDomain - login domain, empty string means login to a domain
  136. * the same as lpszServerName
  137. * xRes, yRes - clients resolution, 0x0 - default
  138. * ConnectFlags -
  139. * - low speed (compression) option
  140. * - cache the bitmaps to the disc option
  141. * - connection context allocated in this function
  142. * Return value:
  143. * Error message. NULL on success
  144. * Called by:
  145. * SCConnect
  146. --*/
  147. PROTOCOLAPI
  148. LPCSTR
  149. SMCAPI
  150. SCConnectEx(
  151. LPCWSTR lpszServerName,
  152. LPCWSTR lpszUserName,
  153. LPCWSTR lpszPassword,
  154. LPCWSTR lpszDomain,
  155. LPCWSTR lpszShell,
  156. int xRes,
  157. int yRes,
  158. int ConnectionFlags,
  159. int Bpp,
  160. int AudioOpts,
  161. PCONNECTINFO *ppCI)
  162. {
  163. // HWND hDialog;
  164. HWND hClient;
  165. // HWND hConnect;
  166. HWND hContainer, hInput, hOutput;
  167. STARTUPINFO si;
  168. PROCESS_INFORMATION procinfo;
  169. LPCSTR rv = NULL;
  170. int trys;
  171. WCHAR szCommandLine[ 4 * MAX_STRING_LENGTH ];
  172. LPCSTR szDiscon;
  173. UINT xxRes, yyRes;
  174. CHAR myServerName[ MAX_STRING_LENGTH ];
  175. // Correct the resolution
  176. if (xRes >= 1600 && yRes >= 1200) {xxRes = 1600; yyRes = 1200;}
  177. else if (xRes >= 1280 && yRes >= 1024) {xxRes = 1280; yyRes = 1024;}
  178. else if (xRes >= 1024 && yRes >= 768) {xxRes = 1024; yyRes = 768;}
  179. else if (xRes >= 800 && yRes >= 600) {xxRes = 800; yyRes = 600;}
  180. else {xxRes = 640; yyRes = 480;}
  181. *ppCI = NULL;
  182. for (trys = 60; trys && !g_hWindow; trys--)
  183. Sleep(1000);
  184. if (!g_hWindow)
  185. {
  186. TRACE((ERROR_MESSAGE, "Panic !!! Feedback window is not created\n"));
  187. rv = ERR_WAIT_FAIL_TIMEOUT;
  188. goto exitpt;
  189. }
  190. *ppCI = (PCONNECTINFO)malloc(sizeof(**ppCI));
  191. if (!*ppCI)
  192. {
  193. TRACE((ERROR_MESSAGE,
  194. "Couldn't allocate %d bytes memory\n",
  195. sizeof(**ppCI)));
  196. rv = ERR_ALLOCATING_MEMORY;
  197. goto exitpt;
  198. }
  199. memset(*ppCI, 0, sizeof(**ppCI));
  200. (*ppCI)->pConfigInfo = (PCONFIGINFO)malloc(sizeof(CONFIGINFO));
  201. if (!((*ppCI)->pConfigInfo))
  202. {
  203. TRACE((ERROR_MESSAGE,
  204. "Couldn't allocate %d bytes memory\n");
  205. sizeof(**ppCI));
  206. rv = ERR_ALLOCATING_MEMORY;
  207. goto exiterr;
  208. }
  209. WideCharToMultiByte(CP_ACP,0,lpszServerName,-1,myServerName, sizeof( myServerName ), NULL, NULL);
  210. _FillConfigInfo((*ppCI)->pConfigInfo);
  211. //
  212. // check for console extension
  213. //
  214. if ( 0 != ( ConnectionFlags & TSFLAG_CONSOLE ))
  215. {
  216. (*ppCI)->bConsole = TRUE;
  217. rv = _SCConsConnect(
  218. lpszServerName,
  219. lpszUserName,
  220. lpszPassword,
  221. lpszDomain,
  222. xRes, yRes,
  223. *ppCI
  224. );
  225. if ( NULL == rv )
  226. goto exitpt;
  227. else {
  228. //
  229. // the trick here is the console will be unloaded after
  230. // several clock ticks, so we need to replace the error with
  231. // generic one
  232. //
  233. TRACE((ERROR_MESSAGE, "Error in console dll (replacing): %s\n", rv ));
  234. rv = ERR_CONSOLE_GENERIC;
  235. goto exiterr;
  236. }
  237. }
  238. (*ppCI)->OwnerThreadId = GetCurrentThreadId();
  239. // Check in what mode the client will be executed
  240. // if the server name starts with '\'
  241. // then tclient.dll will wait until some remote client
  242. // is connected (aka RCLX mode)
  243. // otherwise start the client on the same machine
  244. // running tclient.dll (smclient)
  245. if (*lpszServerName != L'\\')
  246. {
  247. // This is local mode, start the RDP client process
  248. FillMemory(&si, sizeof(si), 0);
  249. si.cb = sizeof(si);
  250. si.wShowWindow = SW_SHOWMINIMIZED;
  251. SetAllowBackgroundInput();
  252. if ( (*ppCI)->pConfigInfo->UseRegistry )
  253. _SetClientRegistry(lpszServerName,
  254. lpszShell,
  255. lpszUserName,
  256. lpszPassword,
  257. lpszDomain,
  258. xxRes, yyRes,
  259. Bpp,
  260. AudioOpts,
  261. ppCI,
  262. ConnectionFlags,
  263. (*ppCI)->pConfigInfo->KeyboardHook);
  264. if ( 0 != wcslen( (*ppCI)->pConfigInfo->strCmdLineFmt ))
  265. {
  266. ConstructCmdLine(
  267. lpszServerName,
  268. lpszUserName,
  269. lpszPassword,
  270. lpszDomain,
  271. lpszShell,
  272. xRes,
  273. yRes,
  274. ConnectionFlags,
  275. szCommandLine,
  276. sizeof( szCommandLine ) / sizeof( *szCommandLine) - 1,
  277. (*ppCI)->pConfigInfo
  278. );
  279. }
  280. else {
  281. _snwprintf(szCommandLine, sizeof(szCommandLine)/sizeof(WCHAR),
  282. #ifdef _WIN64
  283. L"%s /CLXDLL=CLXTSHAR.DLL /CLXCMDLINE=%s%I64d %s " REG_FORMAT,
  284. #else // !_WIN64
  285. L"%s /CLXDLL=CLXTSHAR.DLL /CLXCMDLINE=%s%d %s " REG_FORMAT,
  286. #endif // _WIN64
  287. (*ppCI)->pConfigInfo->strClientImg, _T(_HWNDOPT),
  288. (LONG_PTR)g_hWindow,
  289. (ConnectionFlags & TSFLAG_RCONSOLE)?L"-console":L"",
  290. GetCurrentProcessId(), GetCurrentThreadId());
  291. }
  292. szCommandLine[ sizeof(szCommandLine)/sizeof(WCHAR) - 1 ] = 0;
  293. (*ppCI)->dead = FALSE;
  294. _AddToClientQ(*ppCI);
  295. if (!CreateProcess(NULL,
  296. szCommandLine,
  297. NULL, // Security attribute for process
  298. NULL, // Security attribute for thread
  299. FALSE, // Inheritance - no
  300. 0, // Creation flags
  301. NULL, // Environment
  302. NULL, // Current dir
  303. &si,
  304. &procinfo))
  305. {
  306. TRACE((ERROR_MESSAGE,
  307. "Error creating process (szCmdLine=%S), GetLastError=0x%x\n",
  308. szCommandLine, GetLastError()));
  309. procinfo.hProcess = procinfo.hThread = NULL;
  310. rv = ERR_CREATING_PROCESS;
  311. goto exiterr;
  312. }
  313. (*ppCI)->hProcess = procinfo.hProcess;
  314. (*ppCI)->hThread = procinfo.hThread;
  315. (*ppCI)->lProcessId = procinfo.dwProcessId;
  316. (*ppCI)->dwThreadId = procinfo.dwThreadId;
  317. if (wcslen((*ppCI)->pConfigInfo->strDebugger))
  318. // attempt to launch a "debugger"
  319. {
  320. PROCESS_INFORMATION debuggerproc_info;
  321. _snwprintf( szCommandLine,
  322. sizeof(szCommandLine)/sizeof(WCHAR),
  323. (*ppCI)->pConfigInfo->strDebugger,
  324. procinfo.dwProcessId
  325. );
  326. szCommandLine[ sizeof(szCommandLine)/sizeof(WCHAR) - 1 ] = 0;
  327. FillMemory(&si, sizeof(si), 0);
  328. si.cb = sizeof(si);
  329. if (CreateProcess(
  330. NULL,
  331. szCommandLine,
  332. NULL,
  333. NULL,
  334. FALSE,
  335. 0,
  336. NULL,
  337. NULL,
  338. &si,
  339. &debuggerproc_info
  340. ))
  341. {
  342. TRACE((INFO_MESSAGE, "Debugger is started\n"));
  343. CloseHandle(debuggerproc_info.hProcess);
  344. CloseHandle(debuggerproc_info.hThread);
  345. } else {
  346. TRACE((WARNING_MESSAGE,
  347. "Can't start debugger. GetLastError=%d\n",
  348. GetLastError()));
  349. }
  350. }
  351. rv = Wait4Connect(*ppCI);
  352. if (rv || (*ppCI)->dead)
  353. {
  354. (*ppCI)->dead = TRUE;
  355. TRACE((WARNING_MESSAGE, "Client can't connect\n"));
  356. rv = ERR_CONNECTING;
  357. goto exiterr;
  358. }
  359. hClient = (*ppCI)->hClient;
  360. if ( NULL == hClient )
  361. {
  362. trys = 120; // 2 minutes
  363. do {
  364. hClient = _FindTopWindow((*ppCI)->pConfigInfo->strMainWindowClass,
  365. NULL,
  366. procinfo.dwProcessId);
  367. if (!hClient)
  368. {
  369. Sleep(1000);
  370. trys --;
  371. }
  372. } while(!hClient && trys);
  373. if (!trys)
  374. {
  375. TRACE((WARNING_MESSAGE, "Can't connect"));
  376. rv = ERR_CONNECTING;
  377. goto exiterr;
  378. }
  379. }
  380. // Find the clients child windows
  381. trys = 240; // 2 min
  382. do {
  383. hContainer = _FindWindow(hClient, NULL, NAME_CONTAINERCLASS);
  384. hInput = _FindWindow(hContainer, NULL, NAME_INPUT);
  385. hOutput = _FindWindow(hContainer, NULL, NAME_OUTPUT);
  386. if (!hContainer || !hInput || !hOutput)
  387. {
  388. TRACE((INFO_MESSAGE, "Can't get child windows. Retry"));
  389. Sleep(500);
  390. trys--;
  391. }
  392. } while ((!hContainer || !hInput || !hOutput) && trys);
  393. if (!trys)
  394. {
  395. TRACE((WARNING_MESSAGE, "Can't find child windows"));
  396. rv = ERR_CONNECTING;
  397. goto exiterr;
  398. }
  399. TRACE((INFO_MESSAGE, "hClient = 0x%x\n", hClient));
  400. TRACE((INFO_MESSAGE, "hContainer= 0x%x\n", hContainer));
  401. TRACE((INFO_MESSAGE, "hInput = 0x%x\n", hInput));
  402. TRACE((INFO_MESSAGE, "hOutput = 0x%x\n", hOutput));
  403. (*ppCI)->hClient = hClient;
  404. (*ppCI)->hContainer = hContainer;
  405. (*ppCI)->hInput = hInput;
  406. (*ppCI)->hOutput = hOutput;
  407. #ifdef _RCLX
  408. } else {
  409. // Else what !? This is RCLX mode
  410. // Go in wait mode and wait until some client is connected
  411. // remotely
  412. // set flag in context that this connection works only with remote client
  413. // find the valid server name
  414. while (*lpszServerName && (*lpszServerName) == L'\\')
  415. lpszServerName ++;
  416. TRACE((INFO_MESSAGE,
  417. "A thread in RCLX mode. Wait for some client."
  418. "The target is: %S\n", lpszServerName));
  419. (*ppCI)->dead = FALSE;
  420. (*ppCI)->RClxMode = TRUE;
  421. (*ppCI)->dwThreadId = GetCurrentThreadId();
  422. _AddToClientQ(*ppCI);
  423. rv = _Wait4ConnectTimeout(*ppCI, INFINITE);
  424. if (rv || (*ppCI)->dead)
  425. {
  426. (*ppCI)->dead = TRUE;
  427. TRACE((WARNING_MESSAGE, "Client can't connect to the test controler (us)\n"));
  428. rv = ERR_CONNECTING;
  429. goto exiterr;
  430. } else {
  431. // dwProcessId contains socket. hClient is pointer to RCLX
  432. // context structure, aren't they ?
  433. ASSERT((*ppCI)->lProcessId != INVALID_SOCKET);
  434. ASSERT((*ppCI)->hClient);
  435. TRACE((INFO_MESSAGE, "Client received remote connection\n"));
  436. }
  437. // Next, send connection info to the remote client
  438. // like server to connect to, resolution, etc.
  439. if (!RClx_SendConnectInfo(
  440. (PRCLXCONTEXT)((*ppCI)->hClient),
  441. lpszServerName,
  442. xxRes,
  443. yyRes,
  444. ConnectionFlags))
  445. {
  446. (*ppCI)->dead = TRUE;
  447. TRACE((WARNING_MESSAGE, "Client can't send connection info\n"));
  448. rv = ERR_CONNECTING;
  449. goto exiterr;
  450. }
  451. // Now again wait for connect event
  452. // this time it will be real
  453. rv = Wait4Connect(*ppCI);
  454. if ((*ppCI)->bWillCallAgain)
  455. {
  456. // if so, now the client is disconnected
  457. TRACE((INFO_MESSAGE, "Wait for second call\n"));
  458. (*ppCI)->dead = FALSE;
  459. rv = Wait4Connect(*ppCI);
  460. // Wait for second connect
  461. rv = Wait4Connect(*ppCI);
  462. }
  463. if (rv || (*ppCI)->dead)
  464. {
  465. (*ppCI)->dead = TRUE;
  466. TRACE((WARNING_MESSAGE, "Client(mstsc) can't connect to TS\n"));
  467. rv = ERR_CONNECTING;
  468. goto exiterr;
  469. }
  470. #endif // _RCLX
  471. }
  472. // Save the resolution
  473. (*ppCI)->xRes = xRes;
  474. (*ppCI)->yRes = yRes;
  475. // If username is present
  476. // and no autologon is specified
  477. // try to login
  478. if (wcslen(lpszUserName) && !(*ppCI)->pConfigInfo->Autologon)
  479. {
  480. rv = _Login(*ppCI, lpszServerName, lpszUserName, lpszPassword, lpszDomain);
  481. if (rv)
  482. goto exiterr;
  483. }
  484. exitpt:
  485. return rv;
  486. exiterr:
  487. if (*ppCI)
  488. {
  489. (*ppCI)->bConnectionFailed = TRUE;
  490. if ((szDiscon = SCDisconnect(*ppCI)))
  491. {
  492. TRACE(( WARNING_MESSAGE, "Error disconnecting: %s\n", szDiscon));
  493. }
  494. *ppCI = NULL;
  495. }
  496. return rv;
  497. }
  498. PROTOCOLAPI
  499. LPCSTR
  500. SMCAPI
  501. SCConnect(
  502. LPCWSTR lpszServerName,
  503. LPCWSTR lpszUserName,
  504. LPCWSTR lpszPassword,
  505. LPCWSTR lpszDomain,
  506. IN const int xRes,
  507. IN const int yRes,
  508. PCONNECTINFO *ppCI)
  509. {
  510. INT nConsole;
  511. INT xxRes = xRes;
  512. INT yyRes = yRes;
  513. if ( xRes == -1 && yRes == -1 )
  514. //
  515. // this one goes to the console
  516. //
  517. {
  518. nConsole = TSFLAG_CONSOLE;
  519. xxRes = 0; // there's no change for the console resolution
  520. yyRes = 0;
  521. }
  522. else
  523. nConsole = 0;
  524. return SCConnectEx(
  525. lpszServerName,
  526. lpszUserName,
  527. lpszPassword,
  528. lpszDomain,
  529. NULL, // Default shell (MS Explorer)
  530. xxRes,
  531. yyRes,
  532. g_ConnectionFlags | nConsole, // compression, bmp cache,
  533. // full screen
  534. 8, // bpp
  535. 0, // audio options
  536. ppCI);
  537. }
  538. /*++
  539. * Function:
  540. * SCDisconnect
  541. * Description:
  542. * Called by smclient, when disconnect command is interpreted
  543. * Arguments:
  544. * pCI - connection context
  545. * Return value:
  546. * Error message. NULL on success
  547. * Called by:
  548. * !smclient
  549. --*/
  550. PROTOCOLAPI
  551. LPCSTR
  552. SMCAPI
  553. SCDisconnect(
  554. PCONNECTINFO pCI)
  555. {
  556. LPCSTR rv = NULL;
  557. INT nCloseTime;
  558. INT nCloseTries = 0;
  559. DWORD dw, dwMaxSearch;
  560. DWORD wres;
  561. HWND hYesNo = NULL;
  562. HWND hDiscBox = NULL;
  563. HWND hDialog = NULL;
  564. BOOL bDiscClosed = FALSE;
  565. if (!pCI)
  566. {
  567. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  568. rv = ERR_NULL_CONNECTINFO;
  569. goto exitpt;
  570. }
  571. if ( pCI->bConsole )
  572. {
  573. rv = _SCConsDisconnect( pCI );
  574. if ( NULL != pCI->hConsoleExtension )
  575. FreeLibrary( (HMODULE) pCI->hConsoleExtension );
  576. if ( NULL != pCI->pConfigInfo )
  577. {
  578. free( pCI->pConfigInfo );
  579. pCI->pConfigInfo = NULL;
  580. }
  581. free( pCI );
  582. pCI = NULL;
  583. goto exitpt;
  584. }
  585. if ( NULL == pCI->pConfigInfo )
  586. {
  587. //
  588. // no config, no process
  589. //
  590. goto no_config;
  591. }
  592. nCloseTime = pCI->pConfigInfo->WAIT4STR_TIMEOUT;
  593. //
  594. // if we failed at connection time
  595. // search for "Disconnected" dialog
  596. //
  597. //
  598. if ( pCI->bConnectionFailed )
  599. dwMaxSearch = 10;
  600. else
  601. dwMaxSearch = 1;
  602. for( dw = 0; dw < dwMaxSearch; dw++ )
  603. {
  604. hDiscBox =
  605. _FindTopWindow(NULL,
  606. pCI->pConfigInfo->strDisconnectDialogBox,
  607. pCI->lProcessId);
  608. if ( hDiscBox )
  609. {
  610. TRACE(( INFO_MESSAGE, "Closing disconnect dialog( Visible=%d )\n",
  611. IsWindowVisible( hDiscBox )));
  612. PostMessageA(hDiscBox, WM_CLOSE, __LINE__, 0xc001d00d);
  613. bDiscClosed = TRUE;
  614. break;
  615. } else
  616. Sleep( 1000 );
  617. }
  618. if (
  619. #ifdef _RCLX
  620. !(pCI->RClxMode) &&
  621. #endif // _RCLX
  622. NULL != pCI->hProcess )
  623. {
  624. // Try to close the client window
  625. if ( !bDiscClosed )
  626. {
  627. if (
  628. (hDiscBox =
  629. _FindTopWindow(NULL,
  630. pCI->pConfigInfo->strDisconnectDialogBox,
  631. pCI->lProcessId)))
  632. {
  633. PostMessageA(hDiscBox, WM_CLOSE, __LINE__, 0xc001d00d);
  634. }
  635. else
  636. {
  637. pCI->hClient = _FindTopWindow(pCI->pConfigInfo->strMainWindowClass,
  638. NULL,
  639. pCI->lProcessId);
  640. if ( pCI->hClient )
  641. {
  642. TRACE(( INFO_MESSAGE, "Closing main window (Visible=%d)\n",
  643. IsWindowVisible( pCI->hClient )));
  644. PostMessageA(pCI->hClient, WM_CLOSE, __LINE__, 0xc001d00d);
  645. }
  646. }
  647. }
  648. do {
  649. // search for disconnect dialog and close it
  650. if (!hDialog && !hDiscBox &&
  651. (hDiscBox =
  652. _FindTopWindow(NULL,
  653. pCI->pConfigInfo->strDisconnectDialogBox,
  654. pCI->lProcessId)))
  655. PostMessageA(hDiscBox, WM_CLOSE, __LINE__, 0xc001d00d);
  656. /* can't be in startup dialog UI
  657. // if it is in normal dialog close it
  658. if (!hDiscBox && !hDialog &&
  659. (hDialog =
  660. _FindTopWindow(NULL,
  661. pCI->pConfigInfo->strClientCaption,
  662. pCI->lProcessId)))
  663. PostMessageA(hDialog, WM_CLOSE, __LINE__, 0xc001d00d);
  664. */
  665. // If the client asks whether to close or not
  666. // Answer with 'Yes'
  667. if (!hYesNo)
  668. hYesNo = _FindTopWindow(NULL,
  669. pCI->pConfigInfo->strYesNoShutdown,
  670. pCI->lProcessId);
  671. if (hYesNo)
  672. PostMessageA(hYesNo, WM_KEYDOWN, VK_RETURN, 0);
  673. else if ((nCloseTries % 10) == 5)
  674. {
  675. // On every 10 attempts retry to close the client
  676. if (!pCI->hClient ||
  677. 0 != _wcsicmp(pCI->pConfigInfo->strMainWindowClass, NAME_MAINCLASS))
  678. pCI->hClient = _FindTopWindow(pCI->pConfigInfo->strMainWindowClass,
  679. NULL,
  680. pCI->lProcessId);
  681. if (pCI->hClient)
  682. PostMessageA(pCI->hClient, WM_CLOSE, __LINE__, 0xc001d00d);
  683. }
  684. nCloseTries++;
  685. nCloseTime -= 3000;
  686. } while (
  687. (wres = WaitForSingleObject(pCI->hProcess, 3000)) ==
  688. WAIT_TIMEOUT &&
  689. nCloseTime > 0
  690. );
  691. if (wres == WAIT_TIMEOUT)
  692. {
  693. TRACE((WARNING_MESSAGE,
  694. "Can't close process. WaitForSingleObject timeouts\n"));
  695. TRACE((WARNING_MESSAGE,
  696. "Process #%d will be killed\n",
  697. pCI->lProcessId ));
  698. #if 0
  699. {
  700. PROCESS_INFORMATION debuggerproc_info;
  701. STARTUPINFO si;
  702. CHAR szCommandLine[ MAX_STRING_LENGTH ];
  703. _snprintf( szCommandLine,
  704. sizeof( szCommandLine ),
  705. "ntsd -d %d",
  706. pCI->lProcessId
  707. );
  708. FillMemory(&si, sizeof(si), 0);
  709. si.cb = sizeof(si);
  710. if (CreateProcessA(
  711. NULL,
  712. szCommandLine,
  713. NULL,
  714. NULL,
  715. FALSE,
  716. 0,
  717. NULL,
  718. NULL,
  719. &si,
  720. &debuggerproc_info
  721. ))
  722. {
  723. TRACE((INFO_MESSAGE, "Debugger is started\n"));
  724. CloseHandle(debuggerproc_info.hProcess);
  725. CloseHandle(debuggerproc_info.hThread);
  726. } else {
  727. TRACE((WARNING_MESSAGE,
  728. "Can't start debugger. GetLastError=%d\n",
  729. GetLastError()));
  730. }
  731. }
  732. #else
  733. if (!TerminateProcess(pCI->hProcess, 1))
  734. {
  735. TRACE((WARNING_MESSAGE,
  736. "Can't kill process #%p. GetLastError=%d\n",
  737. pCI->lProcessId, GetLastError()));
  738. }
  739. #endif
  740. }
  741. }
  742. no_config:
  743. if (!_RemoveFromClientQ(pCI))
  744. {
  745. TRACE(( WARNING_MESSAGE,
  746. "Couldn't find CONNECTINFO in the queue\n" ));
  747. }
  748. _CloseConnectInfo(pCI);
  749. exitpt:
  750. return rv;
  751. }
  752. /*++
  753. * Function:
  754. * SCLogoff
  755. * Description:
  756. * Called by smclient, when logoff command is interpreted
  757. * Arguments:
  758. * pCI - connection context
  759. * Return value:
  760. * Error message. NULL on success
  761. * Called by:
  762. * !smclient
  763. --*/
  764. PROTOCOLAPI
  765. LPCSTR
  766. SMCAPI
  767. SCLogoff(
  768. PCONNECTINFO pCI)
  769. {
  770. LPCSTR rv = NULL;
  771. // INT retries = 5;
  772. if (!pCI)
  773. {
  774. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  775. rv = ERR_NULL_CONNECTINFO;
  776. goto exitpt;
  777. }
  778. if (pCI->bConsole)
  779. {
  780. rv = _SCConsLogoff( pCI );
  781. goto exitpt;
  782. }
  783. if (pCI->dead)
  784. {
  785. rv = ERR_CLIENT_IS_DEAD;
  786. goto disconnectpt;
  787. }
  788. /*
  789. do {
  790. // Send Ctrl+Esc
  791. SCSenddata(pCI, WM_KEYDOWN, 17, 1900545);
  792. SCSenddata(pCI, WM_KEYDOWN, 27, 65537);
  793. SCSenddata(pCI, WM_KEYUP, 27, -1073676287);
  794. SCSenddata(pCI, WM_KEYUP, 17, -1071841279);
  795. // Wait for Run... menu
  796. rv = _Wait4Str(pCI,
  797. pCI->pConfigInfo->strStartRun,
  798. pCI->pConfigInfo->WAIT4STR_TIMEOUT/4,
  799. WAIT_STRING);
  800. if (rv)
  801. goto next_retry;
  802. // Send three times Key-Up (scan code 72) and <Enter>
  803. SCSendtextAsMsgs(pCI, pCI->pConfigInfo->strStartLogoff);
  804. rv = _Wait4Str(pCI,
  805. pCI->pConfigInfo->strNTSecurity,
  806. pCI->pConfigInfo->WAIT4STR_TIMEOUT/4,
  807. WAIT_STRING);
  808. next_retry:
  809. retries --;
  810. } while (rv && retries);
  811. if (rv)
  812. goto disconnectpt;
  813. for (retries = 5; retries; retries--) {
  814. SCSendtextAsMsgs(pCI, pCI->pConfigInfo->strNTSecurity_Act);
  815. rv = Wait4Str(pCI, pCI->pConfigInfo->strSureLogoff);
  816. if (!rv) break;
  817. }
  818. */
  819. rv = SCStart( pCI, L"logoff" );
  820. //
  821. // If SCStart fails, send the magic logoff sequence and hope for the
  822. // best. This is a last resort, and should not normally be needed.
  823. //
  824. if (rv)
  825. {
  826. TRACE((WARNING_MESSAGE,
  827. "Unable to find Run window: blindly trying logoff.\n"));
  828. //
  829. // Clear any pop-up windows with Escape.
  830. //
  831. SCSendtextAsMsgs(pCI, L"\\^\\^\\^");
  832. //
  833. // Send Win+R, or Ctrl+Esc and the Run key, `logoff' and Enter.
  834. // Include a delay to wait for the Run window to appear.
  835. //
  836. _SendRunHotkey(pCI, TRUE);
  837. Sleep(10000);
  838. SCSendtextAsMsgs(pCI, L"logoff\\n");
  839. }
  840. // SCSendtextAsMsgs(pCI, g_strSureLogoffAct); // Press enter
  841. rv = Wait4Disconnect(pCI);
  842. if (rv)
  843. {
  844. TRACE((WARNING_MESSAGE, "Can't close the connection\n"));
  845. }
  846. disconnectpt:
  847. rv = SCDisconnect(pCI);
  848. exitpt:
  849. return rv;
  850. }
  851. /*++
  852. * Function:
  853. * SCStart
  854. * Description:
  855. * Called by smclient, when start command is interpreted
  856. * This functions emulates starting an app from Start->Run menu
  857. * on the server side
  858. * Arguments:
  859. * pCI - connection context
  860. * lpszAppName - command line
  861. * Return value:
  862. * Error message. NULL on success
  863. * Called by:
  864. * !smclient
  865. --*/
  866. PROTOCOLAPI
  867. LPCSTR
  868. SMCAPI
  869. SCStart(
  870. PCONNECTINFO pCI, LPCWSTR lpszAppName)
  871. {
  872. LPCSTR waitres = NULL;
  873. int retries;
  874. // int retries2 = 5;
  875. DWORD dwTimeout;
  876. LPCSTR rv = NULL;
  877. if (!pCI)
  878. {
  879. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  880. rv = ERR_NULL_CONNECTINFO;
  881. goto exitpt;
  882. }
  883. if (pCI->bConsole)
  884. {
  885. rv = _SCConsStart( pCI, lpszAppName );
  886. goto exitpt;
  887. }
  888. if (pCI->dead)
  889. {
  890. rv = ERR_CLIENT_IS_DEAD;
  891. goto exitpt;
  892. }
  893. dwTimeout = 10000; // start the timeout of 10 secs
  894. // Try to start run menu
  895. do {
  896. // Press Ctrl+Esc
  897. for (retries = 0; retries < 5; retries += 1) {
  898. TRACE((ALIVE_MESSAGE, "Start: Sending Ctrl+Esc\n"));
  899. SCSenddata(pCI, WM_KEYDOWN, 17, 1900545);
  900. SCSenddata(pCI, WM_KEYDOWN, 27, 65537);
  901. SCSenddata(pCI, WM_KEYUP, 27, -1073676287);
  902. SCSenddata(pCI, WM_KEYUP, 17, -1071841279);
  903. // If the last wait was unsuccessfull increase the timeout
  904. if (waitres)
  905. dwTimeout += 2000;
  906. // Wait for Run... menu
  907. waitres = _Wait4Str(pCI,
  908. pCI->pConfigInfo->strStartRun,
  909. dwTimeout,
  910. WAIT_STRING);
  911. if (waitres)
  912. {
  913. TRACE((INFO_MESSAGE, "Start: Start menu didn't appear. Retrying\n"));
  914. } else {
  915. TRACE((ALIVE_MESSAGE, "Start: Got the start menu\n"));
  916. break;
  917. }
  918. }
  919. //
  920. // If the Start menu appeared, send the character to open the run
  921. // window.
  922. //
  923. if (!waitres)
  924. {
  925. // sometimes this message is sent before the start menu has the
  926. // input focus therefore let's wait for sometime
  927. Sleep(2000);
  928. TRACE((ALIVE_MESSAGE,
  929. "Start: Sending shortcut 'r' for Run command\n"))
  930. // press 'R' for Run...
  931. SCSendtextAsMsgs(pCI, pCI->pConfigInfo->strStartRun_Act);
  932. }
  933. //
  934. // If the Start menu didn't appear, send the run hotkey (Win+R).
  935. //
  936. else
  937. {
  938. TRACE((WARNING_MESSAGE,
  939. "Start: Start menu didn't appear. Trying hotkey\n"));
  940. _SendRunHotkey(pCI, FALSE);
  941. }
  942. TRACE((ALIVE_MESSAGE, "Start: Waiting for the \"Run\" box\n"));
  943. waitres = _Wait4Str(pCI,
  944. pCI->pConfigInfo->strRunBox,
  945. dwTimeout+10000,
  946. WAIT_STRING);
  947. if (waitres)
  948. // No success, press Esc
  949. {
  950. TRACE((INFO_MESSAGE, "Start: Can't get the \"Run\" box. Retrying\n"));
  951. SCSenddata(pCI, WM_KEYDOWN, 27, 65537);
  952. SCSenddata(pCI, WM_KEYUP, 27, -1073676287);
  953. }
  954. } while (waitres && dwTimeout < pCI->pConfigInfo->WAIT4STR_TIMEOUT);
  955. if (waitres)
  956. {
  957. TRACE((WARNING_MESSAGE, "Start: \"Run\" box didn't appear. Giving up\n"));
  958. rv = ERR_COULDNT_OPEN_PROGRAM;
  959. goto exitpt;
  960. }
  961. TRACE((ALIVE_MESSAGE, "Start: Sending the command line\n"));
  962. // Now we have the focus on the "Run" box, send the app name
  963. rv = SCSendtextAsMsgs(pCI, lpszAppName);
  964. // Hit <Enter>
  965. SCSenddata(pCI, WM_KEYDOWN, 13, 1835009);
  966. SCSenddata(pCI, WM_KEYUP, 13, -1071906815);
  967. exitpt:
  968. return rv;
  969. }
  970. // Eventualy, we are going to change the clipboard
  971. // Syncronize this, so no other thread's AV while
  972. // checking the clipboard content
  973. // store 1 for write, 0 for read
  974. static LONG g_ClipOpened = 0;
  975. /*++
  976. * Function:
  977. * SCClipbaord
  978. * Description:
  979. * Called by smclient, when clipboard command is interpreted
  980. * when eClipOp is COPY_TO_CLIPBOARD it copies the lpszFileName to
  981. * the clipboard. If eClipOp is PASTE_FROM_CLIPBOARD it
  982. * checks the clipboard content against the content of lpszFileName
  983. * Arguments:
  984. * pCI - connection context
  985. * eClipOp - clipboard operation. Possible values:
  986. * COPY_TO_CLIPBOARD and PASTE_FROM_CLIPBOARD
  987. * Return value:
  988. * Error message. NULL on success
  989. * Called by:
  990. * !smclient
  991. --*/
  992. PROTOCOLAPI
  993. LPCSTR
  994. SMCAPI
  995. SCClipboard(
  996. PCONNECTINFO pCI, const CLIPBOARDOPS eClipOp, LPCSTR lpszFileName)
  997. {
  998. LPCSTR rv = NULL;
  999. INT hFile = -1;
  1000. LONG clplength = 0;
  1001. UINT uiFormat = 0;
  1002. PBYTE ghClipData = NULL;
  1003. HGLOBAL hNewData = NULL;
  1004. PBYTE pClipData = NULL;
  1005. BOOL bClipboardOpen = FALSE;
  1006. BOOL bFreeClipHandle = TRUE;
  1007. LONG prevOp = 1;
  1008. if (!pCI)
  1009. {
  1010. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1011. rv = ERR_NULL_CONNECTINFO;
  1012. goto exitpt;
  1013. }
  1014. if (pCI->bConsole)
  1015. {
  1016. rv = _SCConsClipboard( pCI, eClipOp, lpszFileName );
  1017. goto exitpt;
  1018. }
  1019. if (pCI->dead)
  1020. {
  1021. rv = ERR_CLIENT_IS_DEAD;
  1022. goto exitpt;
  1023. }
  1024. if (lpszFileName == NULL || !(*lpszFileName))
  1025. {
  1026. // No filename specified, work like an empty clipboard is requested
  1027. if (eClipOp == COPY_TO_CLIPBOARD)
  1028. {
  1029. #ifdef _RCLX
  1030. if (pCI->RClxMode)
  1031. {
  1032. if (!RClx_SendClipboard((PRCLXCONTEXT)(pCI->hClient),
  1033. NULL, 0, 0))
  1034. rv = ERR_COPY_CLIPBOARD;
  1035. } else {
  1036. #endif // _RCLX
  1037. if (!Clp_EmptyClipboard())
  1038. rv = ERR_COPY_CLIPBOARD;
  1039. #ifdef _RCLX
  1040. }
  1041. #endif // _RCLX
  1042. } else if (eClipOp == PASTE_FROM_CLIPBOARD)
  1043. {
  1044. #ifdef _RCLX
  1045. if (pCI->RClxMode)
  1046. {
  1047. if (!RClx_SendClipboardRequest((PRCLXCONTEXT)(pCI->hClient), 0))
  1048. {
  1049. rv = ERR_PASTE_CLIPBOARD;
  1050. goto exitpt;
  1051. }
  1052. if (_Wait4ClipboardTimeout(pCI, pCI->pConfigInfo->WAIT4STR_TIMEOUT))
  1053. {
  1054. rv = ERR_PASTE_CLIPBOARD;
  1055. goto exitpt;
  1056. }
  1057. // We do not expect to receive clipboard data
  1058. // just format ID
  1059. if (!pCI->uiClipboardFormat)
  1060. // if the format is 0, then there's no clipboard
  1061. rv = NULL;
  1062. else
  1063. rv = ERR_PASTE_CLIPBOARD_DIFFERENT_SIZE;
  1064. } else {
  1065. #endif // _RCLX
  1066. if (Clp_CheckEmptyClipboard())
  1067. rv = NULL;
  1068. else
  1069. rv = ERR_PASTE_CLIPBOARD_DIFFERENT_SIZE;
  1070. #ifdef _RCLX
  1071. }
  1072. #endif // _RCLX
  1073. } else {
  1074. TRACE((ERROR_MESSAGE, "SCClipboard: Invalid filename\n"));
  1075. rv = ERR_INVALID_PARAM;
  1076. }
  1077. goto exitpt;
  1078. }
  1079. if (eClipOp == COPY_TO_CLIPBOARD)
  1080. {
  1081. // Open the file for reading
  1082. hFile = _open(lpszFileName, _O_RDONLY|_O_BINARY);
  1083. if (hFile == -1)
  1084. {
  1085. TRACE((ERROR_MESSAGE,
  1086. "Error opening file: %s. errno=%d\n", lpszFileName, errno));
  1087. rv = ERR_COPY_CLIPBOARD;
  1088. goto exitpt;
  1089. }
  1090. // Get the clipboard length (in the file)
  1091. clplength = _filelength(hFile) - sizeof(uiFormat);
  1092. // Get the format
  1093. if (_read(hFile, &uiFormat, sizeof(uiFormat)) != sizeof(uiFormat))
  1094. {
  1095. TRACE((ERROR_MESSAGE,
  1096. "Error reading from file. errno=%d\n", errno));
  1097. rv = ERR_COPY_CLIPBOARD;
  1098. goto exitpt;
  1099. }
  1100. ghClipData = (PBYTE) GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, clplength);
  1101. if (!ghClipData)
  1102. {
  1103. TRACE((ERROR_MESSAGE,
  1104. "Can't allocate %d bytes\n", clplength));
  1105. rv = ERR_COPY_CLIPBOARD;
  1106. goto exitpt;
  1107. }
  1108. pClipData = (PBYTE) GlobalLock( ghClipData );
  1109. if (!pClipData)
  1110. {
  1111. TRACE((ERROR_MESSAGE,
  1112. "Can't lock handle 0x%x\n", ghClipData));
  1113. rv = ERR_COPY_CLIPBOARD;
  1114. goto exitpt;
  1115. }
  1116. if (_read(hFile, pClipData, clplength) != clplength)
  1117. {
  1118. TRACE((ERROR_MESSAGE,
  1119. "Error reading from file. errno=%d\n", errno));
  1120. rv = ERR_COPY_CLIPBOARD;
  1121. goto exitpt;
  1122. }
  1123. GlobalUnlock(ghClipData);
  1124. #ifdef _RCLX
  1125. if (pCI->RClxMode)
  1126. // RCLX mode, send the data to the client's machine
  1127. {
  1128. if (!(pClipData = (PBYTE) GlobalLock(ghClipData)))
  1129. {
  1130. rv = ERR_COPY_CLIPBOARD;
  1131. goto exitpt;
  1132. }
  1133. if (!RClx_SendClipboard((PRCLXCONTEXT)(pCI->hClient),
  1134. pClipData, clplength, uiFormat))
  1135. {
  1136. rv = ERR_COPY_CLIPBOARD;
  1137. goto exitpt;
  1138. }
  1139. } else {
  1140. #endif // _RCLX
  1141. // Local mode, change the clipboard on this machine
  1142. if ((prevOp = InterlockedExchange(&g_ClipOpened, 1)))
  1143. {
  1144. rv = ERR_CLIPBOARD_LOCKED;
  1145. goto exitpt;
  1146. }
  1147. if (!OpenClipboard(NULL))
  1148. {
  1149. TRACE((ERROR_MESSAGE,
  1150. "Can't open the clipboard. GetLastError=%d\n",
  1151. GetLastError()));
  1152. rv = ERR_COPY_CLIPBOARD;
  1153. goto exitpt;
  1154. }
  1155. bClipboardOpen = TRUE;
  1156. // Empty the clipboard, so we'll have only one entry
  1157. EmptyClipboard();
  1158. if (!Clp_SetClipboardData(uiFormat, ghClipData, clplength, &bFreeClipHandle))
  1159. {
  1160. TRACE((ERROR_MESSAGE,
  1161. "SetClipboardData failed. GetLastError=%d\n",
  1162. GetLastError()));
  1163. rv = ERR_COPY_CLIPBOARD;
  1164. goto exitpt;
  1165. }
  1166. #ifdef _RCLX
  1167. }
  1168. #endif // _RCLX
  1169. } else if (eClipOp == PASTE_FROM_CLIPBOARD)
  1170. {
  1171. INT nClipDataSize;
  1172. // Open the file for reading
  1173. hFile = _open(lpszFileName, _O_RDONLY|_O_BINARY);
  1174. if (hFile == -1)
  1175. {
  1176. TRACE((ERROR_MESSAGE,
  1177. "Error opening file: %s. errno=%d\n", lpszFileName, errno));
  1178. rv = ERR_PASTE_CLIPBOARD;
  1179. goto exitpt;
  1180. }
  1181. // Get the clipboard length (in the file)
  1182. clplength = _filelength(hFile) - sizeof(uiFormat);
  1183. // Get the format
  1184. if (_read(hFile, &uiFormat, sizeof(uiFormat)) != sizeof(uiFormat))
  1185. {
  1186. TRACE((ERROR_MESSAGE,
  1187. "Error reading from file. errno=%d\n", errno));
  1188. rv = ERR_PASTE_CLIPBOARD;
  1189. goto exitpt;
  1190. }
  1191. //
  1192. // TODO: For now, set nClipDataSize to avoid warning, but later
  1193. // verify usage is safe.
  1194. //
  1195. nClipDataSize = 0;
  1196. #ifdef _RCLX
  1197. // This piece retrieves the clipboard
  1198. if (pCI->RClxMode)
  1199. // Send request for a clipboard
  1200. {
  1201. if (!RClx_SendClipboardRequest((PRCLXCONTEXT)(pCI->hClient), uiFormat))
  1202. {
  1203. rv = ERR_PASTE_CLIPBOARD;
  1204. goto exitpt;
  1205. }
  1206. if (_Wait4ClipboardTimeout(pCI, pCI->pConfigInfo->WAIT4STR_TIMEOUT))
  1207. {
  1208. rv = ERR_PASTE_CLIPBOARD;
  1209. goto exitpt;
  1210. }
  1211. ghClipData = (PBYTE) pCI->ghClipboard;
  1212. // Get the clipboard size
  1213. nClipDataSize = pCI->nClipboardSize;
  1214. } else {
  1215. #endif // _RCLX
  1216. // retrieve the local clipboard
  1217. if ((prevOp = InterlockedExchange(&g_ClipOpened, 1)))
  1218. {
  1219. rv = ERR_CLIPBOARD_LOCKED;
  1220. goto exitpt;
  1221. }
  1222. if (!OpenClipboard(NULL))
  1223. {
  1224. TRACE((ERROR_MESSAGE,
  1225. "Can't open the clipboard. GetLastError=%d\n",
  1226. GetLastError()));
  1227. rv = ERR_PASTE_CLIPBOARD;
  1228. goto exitpt;
  1229. }
  1230. bClipboardOpen = TRUE;
  1231. // Retrieve the data
  1232. ghClipData = (PBYTE) GetClipboardData(uiFormat);
  1233. if (ghClipData)
  1234. {
  1235. Clp_GetClipboardData(uiFormat,
  1236. ghClipData,
  1237. &nClipDataSize,
  1238. &hNewData);
  1239. bFreeClipHandle = FALSE;
  1240. }
  1241. if (hNewData)
  1242. ghClipData = (PBYTE) hNewData;
  1243. #ifdef _RCLX
  1244. }
  1245. #endif // _RCLX
  1246. if (!ghClipData)
  1247. {
  1248. TRACE((ERROR_MESSAGE,
  1249. "Can't get clipboard data (empty clipboard ?). GetLastError=%d\n",
  1250. GetLastError()));
  1251. rv = ERR_PASTE_CLIPBOARD_EMPTY;
  1252. goto exitpt;
  1253. }
  1254. if (!nClipDataSize)
  1255. TRACE((WARNING_MESSAGE, "Clipboard is empty.\n"));
  1256. pClipData = (PBYTE) GlobalLock(ghClipData);
  1257. if (!pClipData)
  1258. {
  1259. TRACE((ERROR_MESSAGE,
  1260. "Can't lock global mem. GetLastError=%d\n",
  1261. GetLastError()));
  1262. rv = ERR_PASTE_CLIPBOARD;
  1263. goto exitpt;
  1264. }
  1265. #ifdef _RCLX
  1266. // Check if the client is on Win16 platform
  1267. // and the clipboard is paragraph aligned
  1268. // the file size is just bellow this size
  1269. if (pCI->RClxMode &&
  1270. (strstr(pCI->szClientType, "WIN16") != NULL) &&
  1271. ((nClipDataSize % 16) == 0) &&
  1272. ((nClipDataSize - clplength) < 16) &&
  1273. (nClipDataSize != 0))
  1274. {
  1275. // if so, then cut the clipboard size with the difference
  1276. nClipDataSize = clplength;
  1277. }
  1278. else
  1279. #endif // _RCLX
  1280. if (nClipDataSize != clplength)
  1281. {
  1282. TRACE((INFO_MESSAGE, "Different length: file=%d, clipbrd=%d\n",
  1283. clplength, nClipDataSize));
  1284. rv = ERR_PASTE_CLIPBOARD_DIFFERENT_SIZE;
  1285. goto exitpt;
  1286. }
  1287. // compare the data
  1288. {
  1289. BYTE pBuff[1024];
  1290. PBYTE pClp = pClipData;
  1291. UINT nBytes;
  1292. BOOL bEqu = TRUE;
  1293. while (bEqu &&
  1294. (nBytes = _read(hFile, pBuff, sizeof(pBuff))) &&
  1295. nBytes != -1)
  1296. {
  1297. if (memcmp(pBuff, pClp, nBytes))
  1298. bEqu = FALSE;
  1299. pClp += nBytes;
  1300. }
  1301. if (!bEqu)
  1302. {
  1303. TRACE((INFO_MESSAGE, "Clipboard and file are not equal\n"));
  1304. rv = ERR_PASTE_CLIPBOARD_NOT_EQUAL;
  1305. }
  1306. }
  1307. } else
  1308. rv = ERR_UNKNOWN_CLIPBOARD_OP;
  1309. exitpt:
  1310. // Do the cleanup
  1311. // Release the clipboard handle
  1312. if (pClipData)
  1313. GlobalUnlock(ghClipData);
  1314. #ifdef _RCLX
  1315. // free any clipboard received in RCLX mode
  1316. if (pCI->RClxMode && pCI->ghClipboard)
  1317. {
  1318. GlobalFree(pCI->ghClipboard);
  1319. pCI->ghClipboard = NULL;
  1320. }
  1321. else
  1322. #endif // _RCLX
  1323. if (ghClipData && eClipOp == COPY_TO_CLIPBOARD && bFreeClipHandle)
  1324. GlobalFree(ghClipData);
  1325. if (hNewData)
  1326. GlobalFree(hNewData);
  1327. // Close the file
  1328. if (hFile != -1)
  1329. _close(hFile);
  1330. // Close the clipboard
  1331. if (bClipboardOpen)
  1332. CloseClipboard();
  1333. if (!prevOp)
  1334. InterlockedExchange(&g_ClipOpened, 0);
  1335. return rv;
  1336. }
  1337. /*++
  1338. * Function:
  1339. * SCSaveClipboard
  1340. * Description:
  1341. * Save the clipboard in file (szFileName) with
  1342. * format specified in szFormatName
  1343. * Arguments:
  1344. * pCI - connection context
  1345. * szFormatName- format name
  1346. * szFileName - the name of the file to save to
  1347. * Return value:
  1348. * Error message. NULL on success
  1349. * Called by:
  1350. * !perlext
  1351. --*/
  1352. PROTOCOLAPI
  1353. LPCSTR
  1354. SMCAPI
  1355. SCSaveClipboard(
  1356. PCONNECTINFO pCI,
  1357. LPCSTR szFormatName,
  1358. LPCSTR szFileName)
  1359. {
  1360. LPCSTR rv = ERR_SAVE_CLIPBOARD;
  1361. BOOL bClipboardOpen = FALSE;
  1362. UINT nFormatID = 0;
  1363. HGLOBAL ghClipData = NULL;
  1364. HGLOBAL hNewData = NULL;
  1365. INT nClipDataSize;
  1366. CHAR *pClipData = NULL;
  1367. INT hFile = -1;
  1368. LONG prevOp = 1;
  1369. // ++++++ First go thru parameter check
  1370. if (!pCI)
  1371. {
  1372. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1373. rv = ERR_NULL_CONNECTINFO;
  1374. goto exitpt;
  1375. }
  1376. if (pCI->dead)
  1377. {
  1378. rv = ERR_CLIENT_IS_DEAD;
  1379. goto exitpt;
  1380. }
  1381. if (szFormatName == NULL || !(*szFormatName))
  1382. {
  1383. TRACE((ERROR_MESSAGE, "SCClipboard: Invalid format name\n"));
  1384. rv = ERR_INVALID_PARAM;
  1385. goto exitpt;
  1386. }
  1387. if (szFileName == NULL || !(*szFileName))
  1388. {
  1389. TRACE((ERROR_MESSAGE, "SCClipboard: Invalid filename\n"));
  1390. rv = ERR_INVALID_PARAM;
  1391. goto exitpt;
  1392. }
  1393. // ------ End of parameter check
  1394. //
  1395. #ifdef _RCLX
  1396. if (pCI->RClxMode)
  1397. {
  1398. nFormatID = _GetKnownClipboardFormatIDByName(szFormatName);
  1399. if (!nFormatID)
  1400. {
  1401. TRACE((ERROR_MESSAGE, "Can't get the clipboard format ID: %s.\n", szFormatName));
  1402. goto exitpt;
  1403. }
  1404. // Send request for a clipboard
  1405. if (!RClx_SendClipboardRequest((PRCLXCONTEXT)(pCI->hClient), nFormatID))
  1406. {
  1407. rv = ERR_PASTE_CLIPBOARD;
  1408. goto exitpt;
  1409. }
  1410. if (_Wait4ClipboardTimeout(pCI, pCI->pConfigInfo->WAIT4STR_TIMEOUT))
  1411. {
  1412. rv = ERR_PASTE_CLIPBOARD;
  1413. goto exitpt;
  1414. }
  1415. ghClipData = pCI->ghClipboard;
  1416. // Get the clipboard size
  1417. nClipDataSize = pCI->nClipboardSize;
  1418. if (!ghClipData || !nClipDataSize)
  1419. {
  1420. TRACE((WARNING_MESSAGE, "Clipboard is empty.\n"));
  1421. goto exitpt;
  1422. }
  1423. } else {
  1424. #endif // _RCLX
  1425. // local mode
  1426. // Open the clipboard
  1427. if ((prevOp = InterlockedExchange(&g_ClipOpened, 1)))
  1428. {
  1429. rv = ERR_CLIPBOARD_LOCKED;
  1430. goto exitpt;
  1431. }
  1432. if (!OpenClipboard(NULL))
  1433. {
  1434. TRACE((ERROR_MESSAGE, "Can't open the clipboard. GetLastError=%d\n",
  1435. GetLastError()));
  1436. goto exitpt;
  1437. }
  1438. bClipboardOpen = TRUE;
  1439. nFormatID = Clp_GetClipboardFormat(szFormatName);
  1440. if (!nFormatID)
  1441. {
  1442. TRACE((ERROR_MESSAGE, "Can't get the clipboard format: %s.\n", szFormatName));
  1443. goto exitpt;
  1444. }
  1445. TRACE((INFO_MESSAGE, "Format ID: %d(0x%X)\n", nFormatID, nFormatID));
  1446. // Retrieve the data
  1447. ghClipData = GetClipboardData(nFormatID);
  1448. if (!ghClipData)
  1449. {
  1450. TRACE((ERROR_MESSAGE, "Can't get clipboard data. GetLastError=%d\n", GetLastError()));
  1451. goto exitpt;
  1452. }
  1453. Clp_GetClipboardData(nFormatID, ghClipData, &nClipDataSize, &hNewData);
  1454. if (hNewData)
  1455. ghClipData = hNewData;
  1456. if (!nClipDataSize)
  1457. {
  1458. TRACE((WARNING_MESSAGE, "Clipboard is empty.\n"));
  1459. goto exitpt;
  1460. }
  1461. #ifdef _RCLX
  1462. }
  1463. #endif // _RCLX
  1464. pClipData = (char *) GlobalLock(ghClipData);
  1465. if (!pClipData)
  1466. {
  1467. TRACE((ERROR_MESSAGE, "Can't lock global mem. GetLastError=%d\n", GetLastError()));
  1468. goto exitpt;
  1469. }
  1470. // Open the destination file
  1471. hFile = _open(szFileName,
  1472. _O_RDWR|_O_CREAT|_O_BINARY|_O_TRUNC,
  1473. _S_IREAD|_S_IWRITE);
  1474. if (hFile == -1)
  1475. {
  1476. TRACE((ERROR_MESSAGE, "Can't open a file: %s\n", szFileName));
  1477. goto exitpt;
  1478. }
  1479. // First write the format type
  1480. if (_write(hFile, &nFormatID, sizeof(nFormatID)) != sizeof(nFormatID))
  1481. {
  1482. TRACE((ERROR_MESSAGE, "_write failed. errno=%d\n", errno));
  1483. goto exitpt;
  1484. }
  1485. if (_write(hFile, pClipData, nClipDataSize) != (INT)nClipDataSize)
  1486. {
  1487. TRACE((ERROR_MESSAGE, "_write failed. errno=%d\n", errno));
  1488. goto exitpt;
  1489. }
  1490. TRACE((INFO_MESSAGE, "File written successfully. %d bytes written\n", nClipDataSize));
  1491. rv = NULL;
  1492. exitpt:
  1493. // Do the cleanup
  1494. // Close the file
  1495. if (hFile != -1)
  1496. _close(hFile);
  1497. // Release the clipboard handle
  1498. if (pClipData)
  1499. GlobalUnlock(ghClipData);
  1500. if (hNewData)
  1501. GlobalFree(hNewData);
  1502. // Close the clipboard
  1503. if (bClipboardOpen)
  1504. CloseClipboard();
  1505. if (!prevOp)
  1506. InterlockedExchange(&g_ClipOpened, 0);
  1507. #ifdef _RCLX
  1508. // free any clipboard received in RCLX mode
  1509. if (pCI && pCI->RClxMode && pCI->ghClipboard)
  1510. {
  1511. GlobalFree(pCI->ghClipboard);
  1512. pCI->ghClipboard = NULL;
  1513. }
  1514. #endif // _RCLX
  1515. return rv;
  1516. }
  1517. /*++
  1518. * Function:
  1519. * SCSenddata
  1520. * Description:
  1521. * Called by smclient, when senddata command is interpreted
  1522. * Sends an window message to the client
  1523. * Arguments:
  1524. * pCI - connection context
  1525. * uiMessage - the massage Id
  1526. * wParam - word param of the message
  1527. * lParam - long param of the message
  1528. * Return value:
  1529. * Error message. NULL on success
  1530. * Called by:
  1531. * !smclient
  1532. --*/
  1533. PROTOCOLAPI
  1534. LPCSTR
  1535. SMCAPI
  1536. SCSenddata(
  1537. PCONNECTINFO pCI,
  1538. const UINT uiMessage,
  1539. const WPARAM wParam,
  1540. const LPARAM lParam)
  1541. {
  1542. UINT msg = uiMessage;
  1543. LPCSTR rv = NULL;
  1544. if (!pCI)
  1545. {
  1546. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1547. rv = ERR_NULL_CONNECTINFO;
  1548. goto exitpt;
  1549. }
  1550. if (pCI->bConsole)
  1551. {
  1552. rv = _SCConsSenddata( pCI, uiMessage, wParam, lParam );
  1553. goto exitpt;
  1554. }
  1555. if (pCI->dead)
  1556. {
  1557. rv = ERR_CLIENT_IS_DEAD;
  1558. goto exitpt;
  1559. }
  1560. // TRACE((ALIVE_MESSAGE, "Senddata: uMsg=%x wParam=%x lParam=%x\n",
  1561. // uiMessage, wParam, lParam));
  1562. // Determines whether it will
  1563. // send the message to local window
  1564. // or thru RCLX
  1565. #ifdef _RCLX
  1566. if (!pCI->RClxMode)
  1567. {
  1568. #endif // _RCLX
  1569. // Obsolete, a client registry setting "Allow Background Input" asserts
  1570. // that the client will accept the message
  1571. // SetFocus(pCI->hInput);
  1572. // SendMessageA(pCI->hInput, WM_SETFOCUS, 0, 0);
  1573. SendMessageA(pCI->hInput, msg, wParam, lParam);
  1574. #ifdef _RCLX
  1575. } else {
  1576. // RClxMode
  1577. ASSERT(pCI->lProcessId != INVALID_SOCKET);
  1578. ASSERT(pCI->hClient);
  1579. if (!RClx_SendMessage((PRCLXCONTEXT)(pCI->hClient),
  1580. msg, wParam, lParam))
  1581. {
  1582. TRACE((WARNING_MESSAGE,
  1583. "Can't send message thru RCLX\n"));
  1584. }
  1585. }
  1586. #endif // _RCLX
  1587. exitpt:
  1588. return rv;
  1589. }
  1590. PROTOCOLAPI
  1591. LPCSTR
  1592. SMCAPI
  1593. SCClientTerminate(PCONNECTINFO pCI)
  1594. {
  1595. LPCSTR rv = ERR_CLIENTTERMINATE_FAIL;
  1596. if (!pCI)
  1597. {
  1598. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1599. rv = ERR_NULL_CONNECTINFO;
  1600. goto exitpt;
  1601. }
  1602. #ifdef _RCLX
  1603. if (!(pCI->RClxMode))
  1604. {
  1605. #endif // _RCLX
  1606. if (!TerminateProcess(pCI->hProcess, 1))
  1607. {
  1608. TRACE((WARNING_MESSAGE,
  1609. "Can't kill process #%p. GetLastError=%d\n",
  1610. pCI->lProcessId, GetLastError()));
  1611. goto exitpt;
  1612. }
  1613. #ifdef _RCLX
  1614. } else {
  1615. TRACE((WARNING_MESSAGE,
  1616. "ClientTerminate is not supported in RCLX mode yet\n"));
  1617. TRACE((WARNING_MESSAGE, "Using disconnect\n"));
  1618. }
  1619. #endif // _RCLX
  1620. rv = SCDisconnect(pCI);
  1621. exitpt:
  1622. return rv;
  1623. }
  1624. /*++
  1625. * Function:
  1626. * SCGetSessionId
  1627. * Description:
  1628. * Called by smclient, returns the session ID. 0 is invalid, not logged on
  1629. * yet
  1630. * Arguments:
  1631. * pCI - connection context
  1632. * Return value:
  1633. * session id, 0 is invlid value, -1 is returned on NT4 clients
  1634. * Called by:
  1635. * !smclient
  1636. --*/
  1637. PROTOCOLAPI
  1638. UINT
  1639. SMCAPI
  1640. SCGetSessionId(PCONNECTINFO pCI)
  1641. {
  1642. UINT rv = 0;
  1643. if (!pCI)
  1644. {
  1645. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1646. goto exitpt;
  1647. }
  1648. if (pCI->dead)
  1649. {
  1650. goto exitpt;
  1651. }
  1652. rv = pCI->uiSessionId;
  1653. exitpt:
  1654. return rv;
  1655. }
  1656. /*++
  1657. * Function:
  1658. * SCCheck
  1659. * Description:
  1660. * Called by smclient, when check command is interpreted
  1661. * Arguments:
  1662. * pCI - connection context
  1663. * lpszCommand - command name
  1664. * lpszParam - command parameter
  1665. * Return value:
  1666. * Error message. NULL on success. Exceptions are GetDisconnectReason and
  1667. * GetWait4MultipleStrResult
  1668. * Called by:
  1669. * !smclient
  1670. --*/
  1671. PROTOCOLAPI
  1672. LPCSTR
  1673. SMCAPI
  1674. SCCheck(PCONNECTINFO pCI, LPCSTR lpszCommand, LPCWSTR lpszParam)
  1675. {
  1676. LPCSTR rv = ERR_INVALID_COMMAND;
  1677. if (!pCI)
  1678. {
  1679. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1680. rv = ERR_NULL_CONNECTINFO;
  1681. goto exitpt;
  1682. }
  1683. if (pCI->bConsole)
  1684. {
  1685. rv = _SCConsCheck( pCI, lpszCommand, lpszParam );
  1686. goto exitpt;
  1687. }
  1688. if (pCI->dead)
  1689. {
  1690. rv = ERR_CLIENT_IS_DEAD;
  1691. goto exitpt;
  1692. }
  1693. if ( !_stricmp(lpszCommand, "Wait4Str"))
  1694. rv = Wait4Str(pCI, lpszParam);
  1695. else if (!_stricmp(lpszCommand, "Wait4Disconnect"))
  1696. rv = Wait4Disconnect(pCI);
  1697. else if (!_stricmp(lpszCommand, "RegisterChat"))
  1698. rv = RegisterChat(pCI, lpszParam);
  1699. else if (!_stricmp(lpszCommand, "UnregisterChat"))
  1700. rv = UnregisterChat(pCI, lpszParam);
  1701. else if (!_stricmp(lpszCommand, "GetDisconnectReason"))
  1702. rv = GetDisconnectReason(pCI);
  1703. else if (!_stricmp(lpszCommand, "Wait4StrTimeout"))
  1704. rv = Wait4StrTimeout(pCI, lpszParam);
  1705. else if (!_stricmp(lpszCommand, "Wait4MultipleStr"))
  1706. rv = Wait4MultipleStr(pCI, lpszParam);
  1707. else if (!_stricmp(lpszCommand, "Wait4MultipleStrTimeout"))
  1708. rv = Wait4MultipleStrTimeout(pCI, lpszParam);
  1709. else if (!_stricmp(lpszCommand, "GetWait4MultipleStrResult"))
  1710. rv = GetWait4MultipleStrResult(pCI, lpszParam);
  1711. else if (!_stricmp(lpszCommand, "SwitchToProcess"))
  1712. rv = SCSwitchToProcess(pCI, lpszParam);
  1713. else if (!_stricmp(lpszCommand, "SetClientTopmost"))
  1714. rv = SCSetClientTopmost(pCI, lpszParam);
  1715. else if (!_strnicmp(lpszCommand, "call:", 5))
  1716. rv = SCCallDll(pCI, lpszCommand + 5, lpszParam);
  1717. /* **New** */
  1718. else if (!_stricmp(lpszCommand, "DoUntil" ))
  1719. rv = SCDoUntil( pCI, lpszParam );
  1720. exitpt:
  1721. return rv;
  1722. }
  1723. /*
  1724. * Extensions and help functions
  1725. */
  1726. /*++
  1727. * Function:
  1728. * Wait4Disconnect
  1729. * Description:
  1730. * Waits until the client is disconnected
  1731. * Arguments:
  1732. * pCI - connection context
  1733. * Return value:
  1734. * Error message. NULL on success
  1735. * Called by:
  1736. * SCCheck, SCLogoff
  1737. --*/
  1738. LPCSTR Wait4Disconnect(PCONNECTINFO pCI)
  1739. {
  1740. WAIT4STRING Wait;
  1741. LPCSTR rv = NULL;
  1742. if (!pCI)
  1743. {
  1744. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1745. rv = ERR_NULL_CONNECTINFO;
  1746. goto exitpt;
  1747. }
  1748. if (pCI->dead)
  1749. {
  1750. rv = ERR_CLIENT_IS_DEAD;
  1751. goto exitpt;
  1752. }
  1753. memset(&Wait, 0, sizeof(Wait));
  1754. Wait.evWait = CreateEvent(NULL, //security
  1755. TRUE, //manual
  1756. FALSE, //initial state
  1757. NULL); //name
  1758. Wait.lProcessId = pCI->lProcessId;
  1759. Wait.pOwner = pCI;
  1760. Wait.WaitType = WAIT_DISC;
  1761. rv = _WaitSomething(pCI, &Wait, pCI->pConfigInfo->WAIT4STR_TIMEOUT);
  1762. if (!rv)
  1763. {
  1764. TRACE(( INFO_MESSAGE, "Client is disconnected\n"));
  1765. }
  1766. CloseHandle(Wait.evWait);
  1767. exitpt:
  1768. return rv;
  1769. }
  1770. /*++
  1771. * Function:
  1772. * Wait4Connect
  1773. * Description:
  1774. * Waits until the client is connect
  1775. * Arguments:
  1776. * pCI - connection context
  1777. * Return value:
  1778. * Error message, NULL on success
  1779. * Called by:
  1780. * SCCOnnect
  1781. --*/
  1782. LPCSTR Wait4Connect(PCONNECTINFO pCI)
  1783. {
  1784. return (_Wait4ConnectTimeout(pCI, pCI->pConfigInfo->CONNECT_TIMEOUT));
  1785. }
  1786. /*++
  1787. * Function:
  1788. * _Wait4ConnectTimeout
  1789. * Description:
  1790. * Waits until the client is connect
  1791. * Arguments:
  1792. * pCI - connection context
  1793. * dwTimeout - timeout value
  1794. * Return value:
  1795. * Error message, NULL on success
  1796. * Called by:
  1797. * SCConnect
  1798. --*/
  1799. LPCSTR _Wait4ConnectTimeout(PCONNECTINFO pCI, DWORD dwTimeout)
  1800. {
  1801. WAIT4STRING Wait;
  1802. LPCSTR rv = NULL;
  1803. if (!pCI)
  1804. {
  1805. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1806. rv = ERR_NULL_CONNECTINFO;
  1807. goto exitpt;
  1808. }
  1809. memset(&Wait, 0, sizeof(Wait));
  1810. Wait.evWait = CreateEvent(NULL, //security
  1811. TRUE, //manual
  1812. FALSE, //initial state
  1813. NULL); //name
  1814. Wait.lProcessId = pCI->lProcessId;
  1815. Wait.pOwner = pCI;
  1816. Wait.WaitType = WAIT_CONN;
  1817. rv = _WaitSomething(pCI, &Wait, dwTimeout);
  1818. if (!rv)
  1819. {
  1820. TRACE(( INFO_MESSAGE, "Client is connected\n"));
  1821. }
  1822. CloseHandle(Wait.evWait);
  1823. exitpt:
  1824. return rv;
  1825. }
  1826. /*++
  1827. * Function:
  1828. * _Wait4ClipboardTimeout
  1829. * Description:
  1830. * Waits until clipboard response is received from RCLX module
  1831. * Arguments:
  1832. * pCI - connection context
  1833. * dwTimeout - timeout value
  1834. * Return value:
  1835. * Error message, NULL on success
  1836. * Called by:
  1837. * SCClipboard
  1838. --*/
  1839. LPCSTR _Wait4ClipboardTimeout(PCONNECTINFO pCI, DWORD dwTimeout)
  1840. {
  1841. #ifdef _RCLX
  1842. WAIT4STRING Wait;
  1843. #endif
  1844. LPCSTR rv = NULL;
  1845. #ifndef _RCLX
  1846. UNREFERENCED_PARAMETER(dwTimeout);
  1847. #endif
  1848. if (!pCI)
  1849. {
  1850. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1851. rv = ERR_NULL_CONNECTINFO;
  1852. goto exitpt;
  1853. }
  1854. #ifdef _RCLX
  1855. if (!(pCI->RClxMode))
  1856. #endif // _RCLX
  1857. {
  1858. TRACE((WARNING_MESSAGE, "WaitForClipboard: Not in RCLX mode\n"));
  1859. rv = ERR_NULL_CONNECTINFO;
  1860. goto exitpt;
  1861. }
  1862. #ifdef _RCLX
  1863. memset(&Wait, 0, sizeof(Wait));
  1864. Wait.evWait = CreateEvent(NULL, //security
  1865. TRUE, //manual
  1866. FALSE, //initial state
  1867. NULL); //name
  1868. Wait.lProcessId = pCI->lProcessId;
  1869. Wait.pOwner = pCI;
  1870. Wait.WaitType = WAIT_CLIPBOARD;
  1871. rv = _WaitSomething(pCI, &Wait, dwTimeout);
  1872. if (!rv)
  1873. {
  1874. TRACE(( INFO_MESSAGE, "Clipboard received\n"));
  1875. }
  1876. CloseHandle(Wait.evWait);
  1877. #endif // _RCLX
  1878. exitpt:
  1879. return rv;
  1880. }
  1881. /*++
  1882. * Function:
  1883. * GetDisconnectReason
  1884. * Description:
  1885. * Retrieves, if possible, the client error box
  1886. * Arguments:
  1887. * pCI - connection context
  1888. * Return value:
  1889. * The error box message. NULL if not available
  1890. * Called by:
  1891. * SCCheck
  1892. --*/
  1893. LPCSTR GetDisconnectReason(PCONNECTINFO pCI)
  1894. {
  1895. HWND hDiscBox;
  1896. LPCSTR rv = NULL;
  1897. HWND hWnd, hwndTop, hwndNext;
  1898. CHAR classname[128];
  1899. CHAR caption[256];
  1900. if (!pCI)
  1901. {
  1902. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1903. rv = ERR_NULL_CONNECTINFO;
  1904. goto exitpt;
  1905. }
  1906. if (!pCI->dead)
  1907. {
  1908. rv = ERR_CLIENT_IS_DEAD;
  1909. goto exitpt;
  1910. }
  1911. if (strlen(pCI->szDiscReason))
  1912. {
  1913. rv = pCI->szDiscReason;
  1914. goto exitpt;
  1915. }
  1916. hDiscBox = _FindTopWindow(NULL, pCI->pConfigInfo->strDisconnectDialogBox, pCI->lProcessId);
  1917. if (!hDiscBox)
  1918. {
  1919. rv = ERR_NORMAL_EXIT;
  1920. goto exitpt;
  1921. } else {
  1922. TRACE((INFO_MESSAGE, "Found hDiscBox=0x%x", hDiscBox));
  1923. }
  1924. pCI->szDiscReason[0] = 0;
  1925. hWnd = NULL;
  1926. hwndTop = GetWindow(hDiscBox, GW_CHILD);
  1927. if (!hwndTop)
  1928. {
  1929. TRACE((INFO_MESSAGE, "GetWindow failed. hwnd=0x%x\n", hDiscBox));
  1930. goto exitpt;
  1931. }
  1932. hwndNext = hwndTop;
  1933. do {
  1934. hWnd = hwndNext;
  1935. if (!GetClassNameA(hWnd, classname, sizeof(classname)))
  1936. {
  1937. TRACE((INFO_MESSAGE, "GetClassName failed. hwnd=0x%x\n", hWnd));
  1938. goto nextwindow;
  1939. }
  1940. if (!GetWindowTextA(hWnd, caption, sizeof(caption)))
  1941. {
  1942. TRACE((INFO_MESSAGE, "GetWindowText failed. hwnd=0x%x\n"));
  1943. goto nextwindow;
  1944. }
  1945. if (!strcmp(classname, STATIC_CLASS) &&
  1946. strlen(classname) <
  1947. sizeof(pCI->szDiscReason) - strlen(pCI->szDiscReason) - 3)
  1948. {
  1949. strcat(pCI->szDiscReason, caption);
  1950. strcat(pCI->szDiscReason, "\n");
  1951. }
  1952. nextwindow:
  1953. hwndNext = GetNextWindow(hWnd, GW_HWNDNEXT);
  1954. } while (hWnd && hwndNext != hwndTop);
  1955. rv = (LPCSTR)pCI->szDiscReason;
  1956. exitpt:
  1957. return rv;
  1958. }
  1959. /*++
  1960. * Function:
  1961. * Wait4Str
  1962. * Description:
  1963. * Waits for a specific string to come from clients feedback
  1964. * Arguments:
  1965. * pCI - connection context
  1966. * lpszParam - waited string
  1967. * Return value:
  1968. * Error message, NULL on success
  1969. * Called by:
  1970. * SCCheck
  1971. --*/
  1972. LPCSTR Wait4Str(PCONNECTINFO pCI, LPCWSTR lpszParam)
  1973. {
  1974. return _Wait4Str(pCI, lpszParam, pCI->pConfigInfo->WAIT4STR_TIMEOUT, WAIT_STRING);
  1975. }
  1976. /*++
  1977. * Function:
  1978. * Wait4StrTimeout
  1979. * Description:
  1980. * Waits for a specific string to come from clients feedback
  1981. * The timeout is different than default and is specified in
  1982. * lpszParam argument, like:
  1983. * waited_string<->timeout_value
  1984. * Arguments:
  1985. * pCI - connection context
  1986. * lpszParam - waited string and timeout
  1987. * Return value:
  1988. * Error message, NULL on success
  1989. * Called by:
  1990. * SCCheck
  1991. --*/
  1992. LPCSTR Wait4StrTimeout(PCONNECTINFO pCI, LPCWSTR lpszParam)
  1993. {
  1994. WCHAR waitstr[MAX_STRING_LENGTH];
  1995. WCHAR *sep = wcsstr(lpszParam, CHAT_SEPARATOR);
  1996. DWORD dwTimeout;
  1997. LPCSTR rv = NULL;
  1998. if (!pCI)
  1999. {
  2000. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  2001. rv = ERR_NULL_CONNECTINFO;
  2002. goto exitpt;
  2003. }
  2004. if (pCI->dead)
  2005. {
  2006. rv = ERR_CLIENT_IS_DEAD;
  2007. goto exitpt;
  2008. }
  2009. if (!sep)
  2010. {
  2011. TRACE((WARNING_MESSAGE,
  2012. "Wait4StrTiemout: No timeout value. Default applying\n"));
  2013. rv = Wait4Str(pCI, lpszParam);
  2014. } else {
  2015. LONG_PTR len = sep - lpszParam;
  2016. if (len > sizeof(waitstr) - 1)
  2017. len = sizeof(waitstr) - 1;
  2018. wcsncpy(waitstr, lpszParam, len);
  2019. waitstr[len] = 0;
  2020. sep += wcslen(CHAT_SEPARATOR);
  2021. dwTimeout = _wtoi(sep);
  2022. if (!dwTimeout)
  2023. {
  2024. TRACE((WARNING_MESSAGE,
  2025. "Wait4StrTiemout: No timeout value(%s). Default applying\n",
  2026. sep));
  2027. dwTimeout = pCI->pConfigInfo->WAIT4STR_TIMEOUT;
  2028. }
  2029. rv = _Wait4Str(pCI, waitstr, dwTimeout, WAIT_STRING);
  2030. }
  2031. exitpt:
  2032. return rv;
  2033. }
  2034. /*++
  2035. * Function:
  2036. * Wait4MultipleStr
  2037. * Description:
  2038. * Same as Wait4Str, but waits for several strings at once
  2039. * the strings are separated by '|' character
  2040. * Arguments:
  2041. * pCI - connection context
  2042. * lpszParam - waited strings
  2043. * Return value:
  2044. * Error message, NULL on success
  2045. * Called by:
  2046. * SCCheck
  2047. --*/
  2048. LPCSTR Wait4MultipleStr(PCONNECTINFO pCI, LPCWSTR lpszParam)
  2049. {
  2050. return _Wait4Str(pCI, lpszParam, pCI->pConfigInfo->WAIT4STR_TIMEOUT, WAIT_MSTRINGS);
  2051. }
  2052. /*++
  2053. * Function:
  2054. * Wait4MultipleStrTimeout
  2055. * Description:
  2056. * Combination between Wait4StrTimeout and Wait4MultipleStr
  2057. * Arguments:
  2058. * pCI - connection context
  2059. * lpszParam - waited strings and timeout value. Example
  2060. * - "string1|string2|...|stringN<->5000"
  2061. * Return value:
  2062. * Error message, NULL on success
  2063. * Called by:
  2064. * SCCheck
  2065. --*/
  2066. LPCSTR Wait4MultipleStrTimeout(PCONNECTINFO pCI, LPCWSTR lpszParam)
  2067. {
  2068. WCHAR waitstr[MAX_STRING_LENGTH];
  2069. WCHAR *sep = wcsstr(lpszParam, CHAT_SEPARATOR);
  2070. DWORD dwTimeout;
  2071. LPCSTR rv = NULL;
  2072. if (!pCI)
  2073. {
  2074. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  2075. rv = ERR_NULL_CONNECTINFO;
  2076. goto exitpt;
  2077. }
  2078. if (pCI->dead)
  2079. {
  2080. rv = ERR_CLIENT_IS_DEAD;
  2081. goto exitpt;
  2082. }
  2083. pCI->nWait4MultipleStrResult = 0;
  2084. pCI->szWait4MultipleStrResult[0] = 0;
  2085. if (!sep)
  2086. {
  2087. TRACE((WARNING_MESSAGE,
  2088. "Wait4MultipleStrTiemout: No timeout value. Default applying"));
  2089. rv = Wait4MultipleStr(pCI, lpszParam);
  2090. } else {
  2091. LONG_PTR len = sep - lpszParam;
  2092. if (len > sizeof(waitstr) - 1)
  2093. len = sizeof(waitstr) - 1;
  2094. wcsncpy(waitstr, lpszParam, len);
  2095. waitstr[len] = 0;
  2096. sep += wcslen(CHAT_SEPARATOR);
  2097. dwTimeout = _wtoi(sep);
  2098. if (!dwTimeout)
  2099. {
  2100. TRACE((WARNING_MESSAGE,
  2101. "Wait4StrTiemout: No timeout value. Default applying"));
  2102. dwTimeout = pCI->pConfigInfo->WAIT4STR_TIMEOUT;
  2103. }
  2104. rv = _Wait4Str(pCI, waitstr, dwTimeout, WAIT_MSTRINGS);
  2105. }
  2106. exitpt:
  2107. return rv;
  2108. }
  2109. /*++
  2110. * Function:
  2111. * GetWait4MultipleStrResult
  2112. * Description:
  2113. * Retrieves the result from last Wait4MultipleStr call
  2114. * Arguments:
  2115. * pCI - connection context
  2116. * lpszParam - unused
  2117. * Return value:
  2118. * The string, NULL on error
  2119. * Called by:
  2120. * SCCheck
  2121. --*/
  2122. LPCSTR GetWait4MultipleStrResult(PCONNECTINFO pCI, LPCWSTR lpszParam)
  2123. {
  2124. LPCSTR rv = NULL;
  2125. UNREFERENCED_PARAMETER(lpszParam);
  2126. if (!pCI)
  2127. {
  2128. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  2129. rv = ERR_NULL_CONNECTINFO;
  2130. goto exitpt;
  2131. }
  2132. if (*pCI->szWait4MultipleStrResult)
  2133. rv = (LPCSTR)pCI->szWait4MultipleStrResult;
  2134. else
  2135. rv = NULL;
  2136. exitpt:
  2137. return rv;
  2138. }
  2139. LPCSTR
  2140. SMCAPI
  2141. SCGetFeedbackStringA(
  2142. PCONNECTINFO pCI,
  2143. LPSTR szBuff,
  2144. UINT maxBuffChars
  2145. )
  2146. {
  2147. LPWSTR szwBuff;
  2148. LPCSTR rv;
  2149. __try {
  2150. szwBuff = (LPWSTR) _alloca(( maxBuffChars ) * sizeof( WCHAR ));
  2151. } __except( EXCEPTION_EXECUTE_HANDLER )
  2152. {
  2153. szwBuff = NULL;
  2154. }
  2155. if ( NULL == szBuff )
  2156. {
  2157. rv = ERR_ALLOCATING_MEMORY;
  2158. goto exitpt;
  2159. }
  2160. rv = SCGetFeedbackString( pCI, szwBuff, maxBuffChars );
  2161. if ( NULL == rv )
  2162. {
  2163. WideCharToMultiByte(
  2164. CP_UTF8,
  2165. 0,
  2166. szwBuff,
  2167. -1,
  2168. szBuff,
  2169. maxBuffChars,
  2170. NULL,
  2171. NULL );
  2172. }
  2173. exitpt:
  2174. return rv;
  2175. }
  2176. LPCSTR
  2177. SMCAPI
  2178. SCGetFeedbackString(
  2179. PCONNECTINFO pCI,
  2180. LPWSTR szBuff,
  2181. UINT maxBuffChars
  2182. )
  2183. {
  2184. LPCSTR rv = NULL;
  2185. INT nFBpos, nFBsize ;
  2186. if (!pCI)
  2187. {
  2188. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  2189. rv = ERR_NULL_CONNECTINFO;
  2190. goto exitpt;
  2191. }
  2192. if (pCI->dead)
  2193. {
  2194. rv = ERR_CLIENT_IS_DEAD;
  2195. goto exitpt;
  2196. }
  2197. if ( NULL == szBuff )
  2198. {
  2199. TRACE((WARNING_MESSAGE, "SCGetFeedbackString, szBuff is null\n"));
  2200. rv = ERR_INVALID_PARAM;
  2201. goto exitpt;
  2202. }
  2203. if (!maxBuffChars)
  2204. {
  2205. TRACE((WARNING_MESSAGE, "SCGetFeedbackString, maxBuffChars is zero\n"));
  2206. rv = ERR_INVALID_PARAM;
  2207. goto exitpt;
  2208. }
  2209. // Grab the buffer pointers
  2210. EnterCriticalSection(g_lpcsGuardWaitQueue);
  2211. nFBpos = pCI->nFBend + FEEDBACK_SIZE - pCI->nFBsize;
  2212. nFBsize = pCI->nFBsize;
  2213. LeaveCriticalSection(g_lpcsGuardWaitQueue);
  2214. nFBpos %= FEEDBACK_SIZE;
  2215. *szBuff = 0;
  2216. if (!nFBsize)
  2217. // Empty buffer, wait for feedback to receive
  2218. {
  2219. rv = _Wait4Str(pCI, L"", pCI->pConfigInfo->WAIT4STR_TIMEOUT, WAIT_STRING);
  2220. }
  2221. if (!rv)
  2222. // Pickup from buffer
  2223. {
  2224. EnterCriticalSection(g_lpcsGuardWaitQueue);
  2225. // Adjust the buffer pointers
  2226. pCI->nFBsize = pCI->nFBend + FEEDBACK_SIZE - nFBpos - 1;
  2227. pCI->nFBsize %= FEEDBACK_SIZE;
  2228. _snwprintf( szBuff, maxBuffChars, L"%s", pCI->Feedback[nFBpos] );
  2229. LeaveCriticalSection(g_lpcsGuardWaitQueue);
  2230. }
  2231. exitpt:
  2232. return rv;
  2233. }
  2234. VOID
  2235. SMCAPI
  2236. SCFreeMem(
  2237. PVOID pMem
  2238. )
  2239. {
  2240. if ( NULL != pMem )
  2241. free( pMem );
  2242. }
  2243. /*++
  2244. * Function:
  2245. * SCGetFeedback
  2246. * Description:
  2247. * Copies the last received strings to an user buffer
  2248. * Arguments:
  2249. * pCI - connection context
  2250. * pszBufs - pointer to the strings, don't forget to 'SCFreeMem' this buffer
  2251. * pnFBCount - number of strings in *pszBuffs
  2252. * pnFBMaxStrLen - for now MAX_STRING_LENGTH
  2253. * Return value:
  2254. * Error message, NULL on success
  2255. * Called by:
  2256. * * * * EXPORTED * * *
  2257. --*/
  2258. LPCSTR
  2259. SMCAPI
  2260. SCGetFeedback(
  2261. CONNECTINFO *pCI,
  2262. LPWSTR *pszBufs,
  2263. UINT *pnFBCount,
  2264. UINT *pnFBMaxStrLen
  2265. )
  2266. {
  2267. LPWSTR szBufPtr;
  2268. LPWSTR szBufs = NULL;
  2269. LPCSTR rv = NULL;
  2270. INT nFBpos;
  2271. INT nFBindex;
  2272. BOOL bCSAcquired = FALSE;
  2273. if (!pCI)
  2274. {
  2275. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  2276. rv = ERR_NULL_CONNECTINFO;
  2277. goto exitpt;
  2278. }
  2279. if (pCI->dead)
  2280. {
  2281. rv = ERR_CLIENT_IS_DEAD;
  2282. goto exitpt;
  2283. }
  2284. if (NULL == pszBufs || NULL == pnFBCount || NULL == pnFBMaxStrLen)
  2285. {
  2286. TRACE((WARNING_MESSAGE, "SCGetFeedbackStrings, szBufs is null\n"));
  2287. rv = ERR_INVALID_PARAM;
  2288. goto exitpt;
  2289. }
  2290. EnterCriticalSection(g_lpcsGuardWaitQueue);
  2291. bCSAcquired = TRUE;
  2292. if (0 == pCI->nFBsize)
  2293. {
  2294. TRACE((WARNING_MESSAGE, "No strings available\n"));
  2295. rv = ERR_NODATA;
  2296. goto exitpt;
  2297. }
  2298. szBufs = (LPWSTR)malloc(MAX_STRING_LENGTH * pCI->nFBsize * sizeof(WCHAR));
  2299. if(!szBufs)
  2300. {
  2301. TRACE((WARNING_MESSAGE, "Could not allocate buffer array\n"));
  2302. rv = ERR_ALLOCATING_MEMORY;
  2303. goto exitpt;
  2304. }
  2305. // prepare the loop
  2306. nFBpos = pCI->nFBend;
  2307. szBufPtr = szBufs;
  2308. nFBindex = 0;
  2309. do
  2310. {
  2311. if(0 == nFBpos)
  2312. nFBpos = FEEDBACK_SIZE - 1;
  2313. else
  2314. nFBpos --;
  2315. wcscpy(szBufPtr, pCI->Feedback[nFBpos]);
  2316. szBufPtr += MAX_STRING_LENGTH;
  2317. //
  2318. // loop until we have gathered all the strings
  2319. //
  2320. nFBindex++;
  2321. } while(nFBindex < pCI->nFBsize);
  2322. // return back info of strings
  2323. *pnFBCount = pCI->nFBsize;
  2324. *pnFBMaxStrLen = MAX_STRING_LENGTH;
  2325. exitpt:
  2326. if ( NULL != rv )
  2327. {
  2328. if ( NULL != szBufs )
  2329. free( szBufs );
  2330. szBufs = NULL;
  2331. }
  2332. if ( bCSAcquired )
  2333. LeaveCriticalSection(g_lpcsGuardWaitQueue);
  2334. if ( NULL != pszBufs )
  2335. *pszBufs = szBufs;
  2336. return rv;
  2337. }
  2338. /*++
  2339. * Function:
  2340. * SCCallDll
  2341. * Description:
  2342. * Calls an exported dll function
  2343. * Arguments:
  2344. * pCI - connection context
  2345. * lpszDllExport - dll name and function in form:
  2346. * dllname!ExportedFunction
  2347. * the function prototype is:
  2348. * LPCSTR lpfnFunction( PVOID pCI, LPWCSTR lpszParam )
  2349. * lpszParam - parameter passed to the function
  2350. * Return value:
  2351. * the value returned from the call
  2352. * Called by:
  2353. * SCCheck
  2354. --*/
  2355. LPCSTR
  2356. SMCAPI
  2357. SCCallDll(
  2358. PCONNECTINFO pCI,
  2359. LPCSTR lpszDllExport,
  2360. LPCWSTR lpszParam
  2361. )
  2362. {
  2363. LPCSTR rv = NULL;
  2364. PFNSMCDLLIMPORT lpfnImport;
  2365. CHAR lpszDllName[ MAX_STRING_LENGTH ];
  2366. LPSTR lpszImportName;
  2367. LPSTR lpszBang;
  2368. HINSTANCE hLib = NULL;
  2369. DWORD dwDllNameLen;
  2370. if ( NULL == lpszDllExport )
  2371. {
  2372. TRACE((ERROR_MESSAGE, "SCCallDll: DllExport is NULL\n"));
  2373. rv = ERR_INVALID_PARAM;
  2374. goto exitpt;
  2375. }
  2376. if ( NULL == lpszParam )
  2377. {
  2378. TRACE((ERROR_MESSAGE, "SCCallDll: Param is NULL\n"));
  2379. rv = ERR_INVALID_PARAM;
  2380. goto exitpt;
  2381. }
  2382. //
  2383. // split the dll and import names
  2384. //
  2385. lpszBang = strchr( lpszDllExport, '!' );
  2386. if ( NULL == lpszBang )
  2387. {
  2388. TRACE((ERROR_MESSAGE, "SCCallDll: invalid import name (no !)\n"));
  2389. rv = ERR_INVALID_PARAM;
  2390. goto exitpt;
  2391. }
  2392. dwDllNameLen = PtrToLong((PVOID)( lpszBang - lpszDllExport ));
  2393. if ( 0 == dwDllNameLen )
  2394. {
  2395. TRACE((ERROR_MESSAGE, "SCCallDll: dll name is empty string\n"));
  2396. rv = ERR_INVALID_PARAM;
  2397. goto exitpt;
  2398. }
  2399. // copy the dll name
  2400. //
  2401. strncpy( lpszDllName, lpszDllExport, dwDllNameLen );
  2402. lpszDllName[ dwDllNameLen ] = 0;
  2403. // the function name is lpszBang + 1
  2404. //
  2405. lpszImportName = lpszBang + 1;
  2406. TRACE((ALIVE_MESSAGE, "SCCallDll: calling %s!%s(%S)\n",
  2407. lpszDllName, lpszImportName, lpszParam ));
  2408. hLib = LoadLibraryA( lpszDllName );
  2409. if ( NULL == hLib )
  2410. {
  2411. TRACE((ERROR_MESSAGE, "SCCallDll: can't load %s library: %d\n",
  2412. lpszDllName,
  2413. GetLastError()));
  2414. rv = ERR_INVALID_PARAM;
  2415. goto exitpt;
  2416. }
  2417. lpfnImport = (PFNSMCDLLIMPORT)GetProcAddress( hLib, lpszImportName );
  2418. if ( NULL == lpfnImport )
  2419. {
  2420. TRACE((ERROR_MESSAGE, "SCCallDll: can't get the import proc address "
  2421. "of %s. GetLastError=%d\n",
  2422. lpszImportName,
  2423. GetLastError()));
  2424. rv = ERR_INVALID_PARAM;
  2425. goto exitpt;
  2426. }
  2427. __try {
  2428. rv = lpfnImport( pCI, lpszParam );
  2429. }
  2430. __except ( EXCEPTION_EXECUTE_HANDLER )
  2431. {
  2432. TRACE((ERROR_MESSAGE, "SCCallDll: exception 0x%x\n",
  2433. GetExceptionCode()));
  2434. rv = ERR_UNKNOWNEXCEPTION;
  2435. }
  2436. exitpt:
  2437. if ( NULL != hLib )
  2438. FreeLibrary( hLib );
  2439. return rv;
  2440. }
  2441. /*++
  2442. * Function:
  2443. * SCDoUntil
  2444. * Description:
  2445. * Sends keystrokes every 10 seconds until string is received
  2446. * Arguments:
  2447. * pCI - connection context
  2448. * lpszParam - parameter in the form of send_text<->wait_for_this_string
  2449. * Return value:
  2450. * Error message, NULL on success
  2451. * Called by:
  2452. * SCCheck
  2453. --*/
  2454. LPCSTR
  2455. SMCAPI
  2456. SCDoUntil(
  2457. PCONNECTINFO pCI,
  2458. LPCWSTR lpszParam
  2459. )
  2460. {
  2461. LPCSTR rv = NULL;
  2462. DWORD timeout;
  2463. LPWSTR szSendStr, szSepStr, szWaitStr;
  2464. DWORD dwlen;
  2465. if ( NULL == lpszParam )
  2466. {
  2467. TRACE((ERROR_MESSAGE, "SCDoUntil: Param is NULL\n"));
  2468. rv = ERR_INVALID_PARAM;
  2469. goto exitpt;
  2470. }
  2471. //
  2472. // extract the parameters
  2473. //
  2474. szSepStr = wcsstr( lpszParam, CHAT_SEPARATOR );
  2475. if ( NULL == szSepStr )
  2476. {
  2477. TRACE((ERROR_MESSAGE, "SCDoUntil: missing wait string\n" ));
  2478. rv = ERR_INVALID_PARAM;
  2479. goto exitpt;
  2480. }
  2481. szWaitStr = szSepStr + wcslen( CHAT_SEPARATOR );
  2482. if ( 0 == szWaitStr[0] )
  2483. {
  2484. TRACE((ERROR_MESSAGE, "SCDoUntil: wait string is empty\n" ));
  2485. rv = ERR_INVALID_PARAM;
  2486. goto exitpt;
  2487. }
  2488. dwlen = ((DWORD)PtrToLong( (PBYTE)(((PBYTE)szSepStr) - ((PBYTE)lpszParam)) )) / sizeof( WCHAR );
  2489. __try {
  2490. szSendStr = (LPWSTR) _alloca( (dwlen + 1) * sizeof( WCHAR ) );
  2491. } __except( EXCEPTION_EXECUTE_HANDLER )
  2492. {
  2493. szSendStr = NULL;
  2494. }
  2495. if ( NULL == szSendStr )
  2496. {
  2497. TRACE((ERROR_MESSAGE, "SCDoUntil: _alloca failed\n" ));
  2498. rv = ERR_ALLOCATING_MEMORY;
  2499. goto exitpt;
  2500. }
  2501. wcsncpy( szSendStr, lpszParam, dwlen );
  2502. szSendStr[dwlen] = 0;
  2503. timeout = 0;
  2504. while( timeout < pCI->pConfigInfo->WAIT4STR_TIMEOUT )
  2505. {
  2506. if ( pCI->dead )
  2507. {
  2508. rv = ERR_CLIENT_IS_DEAD;
  2509. break;
  2510. }
  2511. SCSendtextAsMsgs( pCI, szSendStr );
  2512. rv = _Wait4Str( pCI, szWaitStr, 3000, WAIT_MSTRINGS );
  2513. if ( NULL == rv )
  2514. break;
  2515. timeout += 3000;
  2516. }
  2517. exitpt:
  2518. return rv;
  2519. }
  2520. #ifdef _RCLX
  2521. /*++
  2522. * Function:
  2523. * _SendRClxData
  2524. * Description:
  2525. * Sends request for data to the client
  2526. * Arguments:
  2527. * pCI - connection context
  2528. * pRClxData - data to send
  2529. * Return value:
  2530. * Error message, NULL on success
  2531. * Called by:
  2532. * SCGetClientScreen
  2533. --*/
  2534. LPCSTR
  2535. _SendRClxData(PCONNECTINFO pCI, PRCLXDATA pRClxData)
  2536. {
  2537. LPCSTR rv = NULL;
  2538. PRCLXCONTEXT pRClxCtx;
  2539. RCLXREQPROLOG Request;
  2540. if (!pCI)
  2541. {
  2542. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  2543. rv = ERR_NULL_CONNECTINFO;
  2544. goto exitpt;
  2545. }
  2546. if (!(pCI->RClxMode))
  2547. {
  2548. TRACE((WARNING_MESSAGE, "_SendRClxData: Not in RCLX mode\n"));
  2549. rv = ERR_NULL_CONNECTINFO;
  2550. goto exitpt;
  2551. }
  2552. pRClxCtx = (PRCLXCONTEXT)pCI->hClient;
  2553. if (!pRClxCtx || pRClxCtx->hSocket == INVALID_SOCKET)
  2554. {
  2555. TRACE((ERROR_MESSAGE, "Not connected yet, RCLX context is null\n"));
  2556. rv = ERR_NULL_CONNECTINFO;
  2557. goto exitpt;
  2558. }
  2559. if (!pRClxData)
  2560. {
  2561. TRACE((ERROR_MESSAGE, "_SendRClxData: Data block is null\n"));
  2562. rv = ERR_INVALID_PARAM;
  2563. goto exitpt;
  2564. }
  2565. Request.ReqType = REQ_DATA;
  2566. Request.ReqSize = pRClxData->uiSize + sizeof(*pRClxData);
  2567. if (!RClx_SendBuffer(pRClxCtx->hSocket, &Request, sizeof(Request)))
  2568. {
  2569. rv = ERR_CLIENT_DISCONNECTED;
  2570. goto exitpt;
  2571. }
  2572. if (!RClx_SendBuffer(pRClxCtx->hSocket, pRClxData, Request.ReqSize))
  2573. {
  2574. rv = ERR_CLIENT_DISCONNECTED;
  2575. goto exitpt;
  2576. }
  2577. exitpt:
  2578. return rv;
  2579. }
  2580. #endif // _RCLX
  2581. #ifdef _RCLX
  2582. /*++
  2583. * Function:
  2584. * _Wait4RClxData
  2585. * Description:
  2586. * Waits for data response from RCLX client
  2587. * Arguments:
  2588. * pCI - connection context
  2589. * dwTimeout - timeout value
  2590. * Return value:
  2591. * Error message, NULL on success
  2592. * Called by:
  2593. * SCGetClientScreen
  2594. --*/
  2595. LPCSTR
  2596. _Wait4RClxDataTimeout(PCONNECTINFO pCI, DWORD dwTimeout)
  2597. {
  2598. WAIT4STRING Wait;
  2599. LPCSTR rv = NULL;
  2600. if (!pCI)
  2601. {
  2602. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  2603. rv = ERR_NULL_CONNECTINFO;
  2604. goto exitpt;
  2605. }
  2606. if (!(pCI->RClxMode))
  2607. {
  2608. TRACE((WARNING_MESSAGE, "_Wait4RClxData: Not in RCLX mode\n"));
  2609. rv = ERR_NULL_CONNECTINFO;
  2610. goto exitpt;
  2611. }
  2612. memset(&Wait, 0, sizeof(Wait));
  2613. Wait.evWait = CreateEvent(NULL, //security
  2614. TRUE, //manual
  2615. FALSE, //initial state
  2616. NULL); //name
  2617. Wait.lProcessId = pCI->lProcessId;
  2618. Wait.pOwner = pCI;
  2619. Wait.WaitType = WAIT_DATA;
  2620. rv = _WaitSomething(pCI, &Wait, dwTimeout);
  2621. if (!rv)
  2622. {
  2623. TRACE(( INFO_MESSAGE, "RCLX data received\n"));
  2624. }
  2625. CloseHandle(Wait.evWait);
  2626. exitpt:
  2627. return rv;
  2628. }
  2629. #endif // _RCLX
  2630. /*++
  2631. * Function:
  2632. * _Wait4Str
  2633. * Description:
  2634. * Waits for string(s) with specified timeout
  2635. * Arguments:
  2636. * pCI - connection context
  2637. * lpszParam - waited string(s)
  2638. * dwTimeout - timeout value
  2639. * WaitType - WAIT_STRING ot WAIT_MSTRING
  2640. * Return value:
  2641. * Error message, NULL on success
  2642. * Called by:
  2643. * SCStart, Wait4Str, Wait4StrTimeout, Wait4MultipleStr
  2644. * Wait4MultipleStrTimeout, GetFeedbackString
  2645. --*/
  2646. LPCSTR _Wait4Str(PCONNECTINFO pCI,
  2647. LPCWSTR lpszParam,
  2648. DWORD dwTimeout,
  2649. WAITTYPE WaitType)
  2650. {
  2651. WAIT4STRING Wait;
  2652. INT_PTR parlen;
  2653. // int i;
  2654. LPCSTR rv = NULL;
  2655. ASSERT(pCI);
  2656. if (pCI->dead)
  2657. {
  2658. rv = ERR_CLIENT_IS_DEAD;
  2659. goto exitpt;
  2660. }
  2661. memset(&Wait, 0, sizeof(Wait));
  2662. // Check the parameter
  2663. parlen = wcslen(lpszParam);
  2664. // Copy the string
  2665. if (parlen > sizeof(Wait.waitstr)/sizeof(WCHAR)-1)
  2666. parlen = sizeof(Wait.waitstr)/sizeof(WCHAR)-1;
  2667. wcsncpy(Wait.waitstr, lpszParam, parlen);
  2668. Wait.waitstr[parlen] = 0;
  2669. Wait.strsize = parlen;
  2670. // Convert delimiters to 0s
  2671. if (WaitType == WAIT_MSTRINGS)
  2672. {
  2673. WCHAR *p = Wait.waitstr;
  2674. while((p = wcschr(p, WAIT_STR_DELIMITER)))
  2675. {
  2676. *p = 0;
  2677. p++;
  2678. }
  2679. }
  2680. Wait.evWait = CreateEvent(NULL, //security
  2681. TRUE, //manual
  2682. FALSE, //initial state
  2683. NULL); //name
  2684. if (!Wait.evWait) {
  2685. TRACE((ERROR_MESSAGE, "Couldn't create event\n"));
  2686. goto exitpt;
  2687. }
  2688. Wait.lProcessId = pCI->lProcessId;
  2689. Wait.pOwner = pCI;
  2690. Wait.WaitType = WaitType;
  2691. TRACE(( INFO_MESSAGE, "Expecting string: %S\n", Wait.waitstr));
  2692. rv = _WaitSomething(pCI, &Wait, dwTimeout);
  2693. TRACE(( INFO_MESSAGE, "String %S received\n", Wait.waitstr));
  2694. if (!rv && pCI->dead)
  2695. {
  2696. rv = ERR_CLIENT_IS_DEAD;
  2697. }
  2698. CloseHandle(Wait.evWait);
  2699. exitpt:
  2700. return rv;
  2701. }
  2702. /*++
  2703. * Function:
  2704. * _WaitSomething
  2705. * Description:
  2706. * Wait for some event: string, connect or disconnect
  2707. * Meanwhile checks for chat sequences
  2708. * Arguments:
  2709. * pCI - connection context
  2710. * pWait - the event function waits for
  2711. * dwTimeout - timeout value
  2712. * Return value:
  2713. * Error message, NULL on success
  2714. * Called by:
  2715. * Wait4Connect, Wait4Disconnect, _Wait4Str
  2716. --*/
  2717. LPCSTR
  2718. _WaitSomething(PCONNECTINFO pCI, PWAIT4STRING pWait, DWORD dwTimeout)
  2719. {
  2720. BOOL bDone = FALSE;
  2721. LPCSTR rv = NULL;
  2722. DWORD waitres;
  2723. ASSERT(pCI || pWait);
  2724. _AddToWaitQueue(pCI, pWait);
  2725. pCI->evWait4Str = pWait->evWait;
  2726. do {
  2727. waitres = WaitForMultipleObjects(
  2728. pCI->nChatNum+1,
  2729. &pCI->evWait4Str,
  2730. FALSE,
  2731. dwTimeout
  2732. );
  2733. if ( waitres <= pCI->nChatNum + WAIT_OBJECT_0)
  2734. {
  2735. if (waitres == WAIT_OBJECT_0)
  2736. {
  2737. bDone = TRUE;
  2738. } else {
  2739. PWAIT4STRING pNWait;
  2740. ASSERT((unsigned)pCI->nChatNum >= waitres - WAIT_OBJECT_0);
  2741. // Here we must send response messages
  2742. waitres -= WAIT_OBJECT_0 + 1;
  2743. ResetEvent(pCI->aevChatSeq[waitres]);
  2744. pNWait = _RetrieveFromWaitQByEvent(pCI->aevChatSeq[waitres]);
  2745. ASSERT(pNWait);
  2746. ASSERT(wcslen(pNWait->respstr));
  2747. TRACE((INFO_MESSAGE,
  2748. "Recieved : [%d]%S\n",
  2749. pNWait->strsize,
  2750. pNWait->waitstr ));
  2751. SCSendtextAsMsgs(pCI, (LPCWSTR)pNWait->respstr);
  2752. }
  2753. } else {
  2754. if (*(pWait->waitstr))
  2755. {
  2756. TRACE((WARNING_MESSAGE,
  2757. "Wait for \"%S\" failed: TIMEOUT\n",
  2758. pWait->waitstr));
  2759. } else {
  2760. TRACE((WARNING_MESSAGE, "Wait failed: TIMEOUT\n"));
  2761. }
  2762. rv = ERR_WAIT_FAIL_TIMEOUT;
  2763. bDone = TRUE;
  2764. }
  2765. } while(!bDone);
  2766. pCI->evWait4Str = NULL;
  2767. _RemoveFromWaitQueue(pWait);
  2768. if (!rv && pCI->dead)
  2769. rv = ERR_CLIENT_IS_DEAD;
  2770. return rv;
  2771. }
  2772. /*++
  2773. * Function:
  2774. * RegisterChat
  2775. * Description:
  2776. * This regiters a wait4str <-> sendtext pair
  2777. * so when we receive a specific string we will send a proper messages
  2778. * lpszParam is kind of: XXXXXX<->YYYYYY
  2779. * XXXXX is the waited string, YYYYY is the respond
  2780. * These command could be nested up to: MAX_WAITING_EVENTS
  2781. * Arguments:
  2782. * pCI - connection context
  2783. * lpszParam - parameter, example:
  2784. * "Connect to existing Windows NT session<->\n"
  2785. * - hit enter when this string is received
  2786. * Return value:
  2787. * Error message, NULL on success
  2788. * Called by:
  2789. * SCCheck, _Login
  2790. --*/
  2791. LPCSTR RegisterChat(PCONNECTINFO pCI, LPCWSTR lpszParam)
  2792. {
  2793. PWAIT4STRING pWait;
  2794. INT_PTR parlen;
  2795. // int i;
  2796. INT_PTR resplen;
  2797. LPCSTR rv = NULL;
  2798. LPCWSTR resp;
  2799. if (!pCI)
  2800. {
  2801. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  2802. rv = ERR_NULL_CONNECTINFO;
  2803. goto exitpt;
  2804. }
  2805. if (!lpszParam)
  2806. {
  2807. TRACE((WARNING_MESSAGE, "Parameter is null\n"));
  2808. rv = ERR_INVALID_PARAM;
  2809. goto exitpt;
  2810. }
  2811. if (pCI->dead)
  2812. {
  2813. rv = ERR_CLIENT_IS_DEAD;
  2814. goto exitpt;
  2815. }
  2816. if (pCI->nChatNum >= MAX_WAITING_EVENTS)
  2817. {
  2818. TRACE(( WARNING_MESSAGE, "RegisterChat: too much waiting strings\n" ));
  2819. goto exitpt;
  2820. }
  2821. // Split the parameter
  2822. resp = wcsstr(lpszParam, CHAT_SEPARATOR);
  2823. // Check the strings
  2824. if (!resp)
  2825. {
  2826. TRACE(( WARNING_MESSAGE, "RegisterChat: invalid parameter\n" ));
  2827. rv = ERR_INVALID_PARAM;
  2828. goto exitpt;
  2829. }
  2830. parlen = wcslen(lpszParam) - wcslen(resp);
  2831. resp += wcslen(CHAT_SEPARATOR);
  2832. if (!parlen)
  2833. {
  2834. TRACE((WARNING_MESSAGE, "RegisterChat empty parameter\n"));
  2835. goto exitpt;
  2836. }
  2837. resplen = wcslen(resp);
  2838. if (!resplen)
  2839. {
  2840. TRACE((WARNING_MESSAGE, "RegisterChat: empty respond string\n" ));
  2841. goto exitpt;
  2842. }
  2843. // Allocate the WAIT4STRING structure
  2844. pWait = (PWAIT4STRING)malloc(sizeof(*pWait));
  2845. if (!pWait)
  2846. {
  2847. TRACE((WARNING_MESSAGE,
  2848. "RegisterChat: can't allocate %d bytes\n",
  2849. sizeof(*pWait) ));
  2850. goto exitpt;
  2851. }
  2852. memset(pWait, 0, sizeof(*pWait));
  2853. // Copy the waited string
  2854. if (parlen > sizeof(pWait->waitstr)/sizeof(WCHAR)-1)
  2855. parlen = sizeof(pWait->waitstr)/sizeof(WCHAR)-1;
  2856. wcsncpy(pWait->waitstr, lpszParam, parlen);
  2857. pWait->waitstr[parlen] = 0;
  2858. pWait->strsize = parlen;
  2859. // Copy the respond string
  2860. if (resplen > sizeof(pWait->respstr)-1)
  2861. resplen = sizeof(pWait->respstr)-1;
  2862. wcsncpy(pWait->respstr, resp, resplen);
  2863. pWait->respstr[resplen] = 0;
  2864. pWait->respsize = resplen;
  2865. pWait->evWait = CreateEvent(NULL, //security
  2866. TRUE, //manual
  2867. FALSE, //initial state
  2868. NULL); //name
  2869. if (!pWait->evWait) {
  2870. TRACE((ERROR_MESSAGE, "Couldn't create event\n"));
  2871. free (pWait);
  2872. goto exitpt;
  2873. }
  2874. pWait->lProcessId = pCI->lProcessId;
  2875. pWait->pOwner = pCI;
  2876. pWait->WaitType = WAIT_STRING;
  2877. // _AddToWaitQNoCheck(pCI, pWait);
  2878. _AddToWaitQueue(pCI, pWait);
  2879. // Add to connection info array
  2880. pCI->aevChatSeq[pCI->nChatNum] = pWait->evWait;
  2881. pCI->nChatNum++;
  2882. exitpt:
  2883. return rv;
  2884. }
  2885. // Remove a WAIT4STRING from waiting Q
  2886. // Param is the waited string
  2887. /*++
  2888. * Function:
  2889. * UnregisterChat
  2890. * Description:
  2891. * Deallocates and removes from waiting Q everithing
  2892. * from RegisterChat function
  2893. * Arguments:
  2894. * pCI - connection context
  2895. * lpszParam - waited string
  2896. * Return value:
  2897. * Error message, NULL on success
  2898. * Called by:
  2899. * SCCheck, _Login
  2900. --*/
  2901. LPCSTR UnregisterChat(PCONNECTINFO pCI, LPCWSTR lpszParam)
  2902. {
  2903. PWAIT4STRING pWait;
  2904. LPCSTR rv = NULL;
  2905. int i;
  2906. if (!pCI)
  2907. {
  2908. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  2909. rv = ERR_NULL_CONNECTINFO;
  2910. goto exitpt;
  2911. }
  2912. if (!lpszParam)
  2913. {
  2914. TRACE((WARNING_MESSAGE, "Parameter is null\n"));
  2915. rv = ERR_INVALID_PARAM;
  2916. goto exitpt;
  2917. }
  2918. pWait = _RemoveFromWaitQIndirect(pCI, lpszParam);
  2919. if (!pWait)
  2920. {
  2921. TRACE((WARNING_MESSAGE,
  2922. "UnregisterChat: can't find waiting string: %S\n",
  2923. lpszParam ));
  2924. goto exitpt;
  2925. }
  2926. i = 0;
  2927. while (i < pCI->nChatNum && pCI->aevChatSeq[i] != pWait->evWait)
  2928. i++;
  2929. ASSERT(i < pCI->nChatNum);
  2930. memmove(pCI->aevChatSeq+i,
  2931. pCI->aevChatSeq+i+1,
  2932. (pCI->nChatNum-i-1)*sizeof(pCI->aevChatSeq[0]));
  2933. pCI->nChatNum--;
  2934. CloseHandle(pWait->evWait);
  2935. free(pWait);
  2936. exitpt:
  2937. return rv;
  2938. }
  2939. /*
  2940. * Returns TRUE if the client is dead
  2941. */
  2942. PROTOCOLAPI
  2943. BOOL
  2944. SMCAPI
  2945. SCIsDead(PCONNECTINFO pCI)
  2946. {
  2947. if (!pCI)
  2948. return TRUE;
  2949. return pCI->dead;
  2950. }
  2951. /*++
  2952. * Function:
  2953. * _CloseConnectInfo
  2954. * Description:
  2955. * Clean all resources for this connection. Close the client
  2956. * Arguments:
  2957. * pCI - connection context
  2958. * Called by:
  2959. * SCDisconnect
  2960. --*/
  2961. VOID
  2962. _CloseConnectInfo(PCONNECTINFO pCI)
  2963. {
  2964. #ifdef _RCLX
  2965. PRCLXDATACHAIN pRClxDataChain, pNext;
  2966. #endif // _RCLX
  2967. ASSERT(pCI);
  2968. _FlushFromWaitQ(pCI);
  2969. // Close All handles
  2970. EnterCriticalSection(g_lpcsGuardWaitQueue);
  2971. /* // not needed, the handle is already closed
  2972. if (pCI->evWait4Str)
  2973. {
  2974. CloseHandle(pCI->evWait4Str);
  2975. pCI->evWait4Str = NULL;
  2976. }
  2977. */
  2978. // Chat events are already closed by FlushFromWaitQ
  2979. // no need to close them
  2980. pCI->nChatNum = 0;
  2981. #ifdef _RCLX
  2982. if (!pCI->RClxMode)
  2983. {
  2984. #endif // _RCLX
  2985. // The client was local, so we have handles opened
  2986. if (pCI->hProcess)
  2987. CloseHandle(pCI->hProcess);
  2988. if (pCI->hThread)
  2989. CloseHandle(pCI->hThread);
  2990. pCI->hProcess = pCI->hThread =NULL;
  2991. #ifdef _RCLX
  2992. } else {
  2993. // Hmmm, RCLX mode. Then disconnect the socket
  2994. if (pCI->hClient)
  2995. RClx_EndRecv((PRCLXCONTEXT)(pCI->hClient));
  2996. pCI->hClient = NULL; // Clean the pointer
  2997. }
  2998. // Clear the clipboard handle (if any)
  2999. if (pCI->ghClipboard)
  3000. {
  3001. GlobalFree(pCI->ghClipboard);
  3002. pCI->ghClipboard = NULL;
  3003. }
  3004. // clear any recevied RCLX data
  3005. pRClxDataChain = pCI->pRClxDataChain;
  3006. while(pRClxDataChain)
  3007. {
  3008. pNext = pRClxDataChain->pNext;
  3009. free(pRClxDataChain);
  3010. pRClxDataChain = pNext;
  3011. }
  3012. #endif // _RCLX
  3013. LeaveCriticalSection(g_lpcsGuardWaitQueue);
  3014. if (
  3015. #ifdef _RCLX
  3016. !pCI->RClxMode &&
  3017. #endif // _RCLX
  3018. NULL != pCI->pConfigInfo &&
  3019. pCI->pConfigInfo->UseRegistry )
  3020. {
  3021. _DeleteClientRegistry(pCI);
  3022. }
  3023. if ( NULL != pCI->pConfigInfo )
  3024. {
  3025. free(pCI->pConfigInfo);
  3026. pCI->pConfigInfo = NULL;
  3027. }
  3028. free(pCI);
  3029. pCI = NULL;
  3030. }
  3031. /*++
  3032. * Function:
  3033. * _Login
  3034. * Description:
  3035. * Emulate login procedure
  3036. * Arguments:
  3037. * pCI - connection context
  3038. * lpszServerName
  3039. * lpszUserName - user name
  3040. * lpszPassword - password
  3041. * lpszDomain - domain name
  3042. * Return value:
  3043. * Error message, NULL on success
  3044. * Called by:
  3045. * SCConnect
  3046. --*/
  3047. LPCSTR
  3048. _Login(PCONNECTINFO pCI,
  3049. LPCWSTR lpszServerName,
  3050. LPCWSTR lpszUserName,
  3051. LPCWSTR lpszPassword,
  3052. LPCWSTR lpszDomain)
  3053. {
  3054. LPCSTR waitres;
  3055. LPCSTR rv = NULL;
  3056. WCHAR szBuff[MAX_STRING_LENGTH];
  3057. #define _LOGON_RETRYS 5
  3058. INT nLogonRetrys = _LOGON_RETRYS;
  3059. UINT nLogonWaitTime;
  3060. INT nFBSize;
  3061. INT nFBEnd;
  3062. ASSERT(pCI);
  3063. szBuff[MAX_STRING_LENGTH - 1] = 0;
  3064. //
  3065. // If a smartcard is being used, wait for the smartcard UI, dismiss it,
  3066. // then wait for the non-smartcard UI.
  3067. //
  3068. if (_IsSmartcardActive())
  3069. {
  3070. waitres = Wait4Str(pCI, pCI->pConfigInfo->strSmartcard);
  3071. if (!waitres)
  3072. {
  3073. SCSendtextAsMsgs(pCI, pCI->pConfigInfo->strSmartcard_Act);
  3074. waitres = Wait4Str(pCI, pCI->pConfigInfo->strNoSmartcard);
  3075. }
  3076. if (waitres)
  3077. {
  3078. TRACE((WARNING_MESSAGE, "Login failed (smartcard)"));
  3079. rv = waitres;
  3080. goto exitpt;
  3081. }
  3082. }
  3083. //
  3084. // Look for a logon string, which will indicate the current state of the
  3085. // logon desktop, and try to get to the logon window.
  3086. //
  3087. retry_logon:
  3088. _snwprintf(szBuff, MAX_STRING_LENGTH - 1, L"%s|%s|%s",
  3089. pCI->pConfigInfo->strWinlogon, pCI->pConfigInfo->strPriorWinlogon,
  3090. pCI->pConfigInfo->strLogonDisabled);
  3091. waitres = Wait4MultipleStr(pCI, szBuff);
  3092. if (!waitres)
  3093. {
  3094. //
  3095. // Prior Winlogon: send the string to begin the logon.
  3096. //
  3097. if (pCI->nWait4MultipleStrResult == 1)
  3098. {
  3099. SCSendtextAsMsgs(pCI, pCI->pConfigInfo->strPriorWinlogon_Act);
  3100. waitres = Wait4Str(pCI, pCI->pConfigInfo->strWinlogon);
  3101. }
  3102. //
  3103. // Dismiss logon-disabled pop-up.
  3104. //
  3105. else if (pCI->nWait4MultipleStrResult == 2)
  3106. {
  3107. SCSendtextAsMsgs(pCI, L"\\n");
  3108. waitres = Wait4Str(pCI, pCI->pConfigInfo->strWinlogon);
  3109. }
  3110. }
  3111. if (waitres)
  3112. {
  3113. TRACE((WARNING_MESSAGE, "Login failed"));
  3114. rv = waitres;
  3115. goto exitpt;
  3116. }
  3117. ConstructLogonString(
  3118. lpszServerName,
  3119. lpszUserName,
  3120. lpszPassword,
  3121. lpszDomain,
  3122. szBuff,
  3123. MAX_STRING_LENGTH,
  3124. pCI->pConfigInfo
  3125. );
  3126. if ( 0 != szBuff[0] )
  3127. {
  3128. SCSendtextAsMsgs( pCI, szBuff );
  3129. } else {
  3130. //
  3131. // do the default login
  3132. // Hit Alt+U to go to user name field
  3133. if ( _LOGON_RETRYS != nLogonRetrys )
  3134. {
  3135. SCSendtextAsMsgs(pCI, pCI->pConfigInfo->strWinlogon_Act);
  3136. SCSendtextAsMsgs(pCI, lpszUserName);
  3137. // Hit <Tab> key
  3138. Sleep(300);
  3139. SCSendtextAsMsgs(pCI, L"\\t");
  3140. }
  3141. Sleep(700);
  3142. SCSendtextAsMsgs(pCI, lpszPassword);
  3143. if ( _LOGON_RETRYS != nLogonRetrys )
  3144. {
  3145. // Hit <Tab> key
  3146. Sleep(300);
  3147. SCSendtextAsMsgs(pCI, L"\\t");
  3148. SCSendtextAsMsgs(pCI, lpszDomain);
  3149. Sleep(300);
  3150. }
  3151. }
  3152. // Retry logon in case of
  3153. // 1. Winlogon is on background
  3154. // 2. Wrong username/password/domain
  3155. // 3. Other
  3156. // Hit <Enter>
  3157. SCSendtextAsMsgs(pCI, L"\\n");
  3158. if ( !pCI->pConfigInfo->LoginWait )
  3159. goto exitpt;
  3160. nLogonWaitTime = 0;
  3161. _snwprintf(szBuff, MAX_STRING_LENGTH - 1, L"%s|%s<->1000",
  3162. pCI->pConfigInfo->strLogonErrorMessage, pCI->pConfigInfo->strSessionListDlg );
  3163. while (!pCI->dead && !pCI->uiSessionId && nLogonWaitTime < pCI->pConfigInfo->CONNECT_TIMEOUT)
  3164. {
  3165. nFBSize = pCI->nFBsize;
  3166. nFBEnd = pCI->nFBend;
  3167. //
  3168. // check if session list dialog is present
  3169. //
  3170. if ( pCI->pConfigInfo->strSessionListDlg[0] )
  3171. {
  3172. waitres = Wait4MultipleStrTimeout(pCI, szBuff);
  3173. if (!waitres)
  3174. {
  3175. TRACE((INFO_MESSAGE, "Session list dialog is present\n" ));
  3176. //
  3177. // restore buffer
  3178. //
  3179. pCI->nFBsize = nFBSize;
  3180. pCI->nFBend = nFBEnd;
  3181. Sleep( 1000 );
  3182. SCSendtextAsMsgs(pCI, L"\\n");
  3183. Sleep( 1000 );
  3184. }
  3185. }
  3186. // Sleep with wait otherwise the chat won't work
  3187. // i.e. this is a hack
  3188. waitres = _Wait4Str(pCI, pCI->pConfigInfo->strLogonErrorMessage, 1000, WAIT_STRING);
  3189. if (!waitres)
  3190. // Error message received
  3191. {
  3192. //
  3193. // restore buffer
  3194. //
  3195. pCI->nFBsize = nFBSize;
  3196. pCI->nFBend = nFBEnd;
  3197. Sleep(1000);
  3198. SCSendtextAsMsgs(pCI, L"\\n");
  3199. Sleep(1000);
  3200. break;
  3201. }
  3202. nLogonWaitTime += 1000;
  3203. }
  3204. if (!pCI->dead && !pCI->uiSessionId)
  3205. {
  3206. TRACE((WARNING_MESSAGE, "Logon sequence failed. Retrying (%d)",
  3207. nLogonRetrys));
  3208. if (nLogonRetrys--)
  3209. goto retry_logon;
  3210. }
  3211. if (!pCI->uiSessionId)
  3212. {
  3213. // Send Enter, just in case we are not logged yet
  3214. SCSendtextAsMsgs(pCI, L"\\n");
  3215. rv = ERR_CANTLOGON;
  3216. }
  3217. exitpt:
  3218. return rv;
  3219. }
  3220. WPARAM _GetVirtualKey(INT scancode)
  3221. {
  3222. if (scancode == 29) // L Control
  3223. return VK_CONTROL;
  3224. else if (scancode == 42) // L Shift
  3225. return VK_SHIFT;
  3226. else if (scancode == 56) // L Alt
  3227. return VK_MENU;
  3228. else
  3229. return MapVirtualKeyA(scancode, 3);
  3230. }
  3231. /*++
  3232. * Function:
  3233. * SCSendtextAsMsgs
  3234. * Description:
  3235. * Converts a string to WM_KEYUP/KEYDOWN messages
  3236. * And sends them thru client window
  3237. * Arguments:
  3238. * pCI - connection context
  3239. * lpszString - the string to be send
  3240. * it can contain the following escape character:
  3241. * \n - Enter, \t - Tab, \^ - Esc, \& - Alt switch up/down
  3242. * \XXX - scancode XXX is down, \*XXX - scancode XXX is up
  3243. * Return value:
  3244. * Error message, NULL on success
  3245. * Called by:
  3246. * SCLogoff, SCStart, _WaitSomething, _Login
  3247. --*/
  3248. PROTOCOLAPI
  3249. LPCSTR
  3250. SMCAPI
  3251. SCSendtextAsMsgs(PCONNECTINFO pCI, LPCWSTR lpszString)
  3252. {
  3253. LPCSTR rv = NULL;
  3254. INT scancode = 0;
  3255. WPARAM vkKey;
  3256. BOOL bShiftDown = FALSE;
  3257. BOOL bAltKey = FALSE;
  3258. BOOL bCtrlKey = FALSE;
  3259. UINT uiMsg;
  3260. LPARAM lParam;
  3261. DWORD dwShiftDown = (ISWIN9X())?SHIFT_DOWN9X:SHIFT_DOWN;
  3262. #define _SEND_KEY(_c_, _m_, _v_, _l_) {/*Sleep(40); */SCSenddata(_c_, _m_, _v_, _l_);}
  3263. if (!pCI)
  3264. {
  3265. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  3266. rv = ERR_NULL_CONNECTINFO;
  3267. goto exitpt;
  3268. }
  3269. if (!lpszString)
  3270. {
  3271. TRACE((ERROR_MESSAGE, "NULL pointer passed to SCSendtextAsMsgs"));
  3272. rv = ERR_INVALID_PARAM;
  3273. goto exitpt;
  3274. }
  3275. TRACE(( INFO_MESSAGE, "Sending: \"%S\"\n", lpszString));
  3276. /*
  3277. // Send KEYUP for the shift(s)
  3278. // CapsLock ???!!!
  3279. _SEND_KEY(pCI, WM_KEYUP, VK_SHIFT,
  3280. WM_KEY_LPARAM(1, 0x2A, 0, 0, 1, 1));
  3281. // Ctrl key
  3282. _SEND_KEY(pCI, WM_KEYUP, VK_CONTROL,
  3283. WM_KEY_LPARAM(1, 0x1D, 0, 0, 1, 1));
  3284. // Alt key
  3285. _SEND_KEY(pCI, WM_SYSKEYUP, VK_MENU,
  3286. WM_KEY_LPARAM(1, 0x38, 0, 0, 1, 1));
  3287. */
  3288. for (;*lpszString; lpszString++)
  3289. {
  3290. if ( pCI->pConfigInfo &&
  3291. pCI->pConfigInfo->bUnicode )
  3292. {
  3293. if ((*lpszString != '\\' && 1 == (rand() & 1) ) || // add randomness and...
  3294. *lpszString > 0x80 // send as unicode if non ascii
  3295. )
  3296. {
  3297. //
  3298. // send unicode character
  3299. //
  3300. uiMsg = (!bAltKey || bCtrlKey)?WM_KEYDOWN:WM_SYSKEYDOWN;
  3301. _SEND_KEY( pCI, uiMsg, VK_PACKET, (*lpszString << 16) );
  3302. uiMsg = (!bAltKey || bCtrlKey)?WM_KEYUP:WM_SYSKEYUP;
  3303. _SEND_KEY( pCI, uiMsg, VK_PACKET, (*lpszString << 16) );
  3304. continue;
  3305. }
  3306. }
  3307. if (*lpszString != '\\') {
  3308. try_again:
  3309. if ((scancode = OemKeyScan(*lpszString)) == 0xffffffff)
  3310. {
  3311. rv = ERR_INVALID_SCANCODE_IN_XLAT;
  3312. goto exitpt;
  3313. }
  3314. // Check the Shift key state
  3315. if ((scancode & dwShiftDown) && !bShiftDown)
  3316. {
  3317. uiMsg = (bAltKey)?WM_SYSKEYDOWN:WM_KEYDOWN;
  3318. _SEND_KEY(pCI, uiMsg, VK_SHIFT,
  3319. WM_KEY_LPARAM(1, 0x2A, 0, (bAltKey)?1:0, 0, 0));
  3320. bShiftDown = TRUE;
  3321. }
  3322. else if (!(scancode & dwShiftDown) && bShiftDown)
  3323. {
  3324. uiMsg = (bAltKey)?WM_SYSKEYUP:WM_KEYUP;
  3325. _SEND_KEY(pCI, uiMsg, VK_SHIFT,
  3326. WM_KEY_LPARAM(1, 0x2A, 0, (bAltKey)?1:0, 1, 1));
  3327. bShiftDown = FALSE;
  3328. }
  3329. } else {
  3330. // Non printable symbols
  3331. lpszString++;
  3332. switch(*lpszString)
  3333. {
  3334. case 'n': scancode = 0x1C; break; // Enter
  3335. case 't': scancode = 0x0F; break; // Tab
  3336. case '^': scancode = 0x01; break; // Esc
  3337. case 'p': Sleep(100); continue; break; // Sleep for 0.1 sec
  3338. case 'P': Sleep(1000); continue; break; // Sleep for 1 sec
  3339. case 'x': SCSendMouseClick(pCI, pCI->xRes/2, pCI->yRes/2); continue; break;
  3340. case '&':
  3341. // Alt key
  3342. if (bAltKey)
  3343. {
  3344. _SEND_KEY(pCI, WM_KEYUP, VK_MENU,
  3345. WM_KEY_LPARAM(1, 0x38, 0, 0, 1, 1));
  3346. } else {
  3347. _SEND_KEY(pCI, WM_SYSKEYDOWN, VK_MENU,
  3348. WM_KEY_LPARAM(1, 0x38, 0, 1, 0, 0));
  3349. }
  3350. bAltKey = !bAltKey;
  3351. continue;
  3352. case '*':
  3353. lpszString ++;
  3354. if (isdigit(*lpszString))
  3355. {
  3356. INT exten;
  3357. scancode = _wtoi(lpszString);
  3358. TRACE((INFO_MESSAGE, "Scancode: %d UP\n", scancode));
  3359. vkKey = _GetVirtualKey(scancode);
  3360. uiMsg = (!bAltKey || bCtrlKey)?WM_KEYUP:WM_SYSKEYUP;
  3361. if (vkKey == VK_MENU)
  3362. bAltKey = FALSE;
  3363. else if (vkKey == VK_CONTROL)
  3364. bCtrlKey = FALSE;
  3365. else if (vkKey == VK_SHIFT)
  3366. bShiftDown = FALSE;
  3367. exten = (_IsExtendedScanCode(scancode))?1:0;
  3368. lParam = WM_KEY_LPARAM(1, scancode, exten, (bAltKey)?1:0, 1, 1);
  3369. if (uiMsg == WM_KEYUP)
  3370. {
  3371. TRACE((INFO_MESSAGE, "WM_KEYUP, 0x%x, 0x%x\n", vkKey, lParam));
  3372. } else {
  3373. TRACE((INFO_MESSAGE, "WM_SYSKEYUP, 0x%x, 0x%x\n", vkKey, lParam));
  3374. }
  3375. _SEND_KEY(pCI, uiMsg, vkKey, lParam);
  3376. while(isdigit(lpszString[1]))
  3377. lpszString++;
  3378. } else {
  3379. lpszString--;
  3380. }
  3381. continue;
  3382. break;
  3383. case 0: continue;
  3384. default:
  3385. if (isdigit(*lpszString))
  3386. {
  3387. INT exten;
  3388. scancode = _wtoi(lpszString);
  3389. TRACE((INFO_MESSAGE, "Scancode: %d DOWN\n", scancode));
  3390. vkKey = _GetVirtualKey(scancode);
  3391. if (vkKey == VK_MENU)
  3392. bAltKey = TRUE;
  3393. else if (vkKey == VK_CONTROL)
  3394. bCtrlKey = TRUE;
  3395. else if (vkKey == VK_SHIFT)
  3396. bShiftDown = TRUE;
  3397. uiMsg = (!bAltKey || bCtrlKey)?WM_KEYDOWN:WM_SYSKEYDOWN;
  3398. exten = (_IsExtendedScanCode(scancode))?1:0;
  3399. lParam = WM_KEY_LPARAM(1, scancode, exten, (bAltKey)?1:0, 0, 0);
  3400. if (uiMsg == WM_KEYDOWN)
  3401. {
  3402. TRACE((INFO_MESSAGE, "WM_KEYDOWN, 0x%x, 0x%x\n", vkKey, lParam));
  3403. } else {
  3404. TRACE((INFO_MESSAGE, "WM_SYSKEYDOWN, 0x%x, 0x%x\n", vkKey, lParam));
  3405. }
  3406. _SEND_KEY(pCI, uiMsg, vkKey, lParam);
  3407. while(isdigit(lpszString[1]))
  3408. lpszString++;
  3409. continue;
  3410. }
  3411. goto try_again;
  3412. }
  3413. }
  3414. vkKey = MapVirtualKeyA(scancode, 3);
  3415. // Remove flag fields
  3416. scancode &= 0xff;
  3417. uiMsg = (!bAltKey || bCtrlKey)?WM_KEYDOWN:WM_SYSKEYDOWN;
  3418. // Send the scancode
  3419. _SEND_KEY(pCI, uiMsg, vkKey,
  3420. WM_KEY_LPARAM(1, scancode, 0, (bAltKey)?1:0, 0, 0));
  3421. uiMsg = (!bAltKey || bCtrlKey)?WM_KEYUP:WM_SYSKEYUP;
  3422. _SEND_KEY(pCI, uiMsg, vkKey,
  3423. WM_KEY_LPARAM(1, scancode, 0, (bAltKey)?1:0, 1, 1));
  3424. }
  3425. // And Alt key
  3426. if (bAltKey)
  3427. _SEND_KEY(pCI, WM_KEYUP, VK_MENU,
  3428. WM_KEY_LPARAM(1, 0x38, 0, 0, 1, 1));
  3429. // Shift up
  3430. if (bShiftDown)
  3431. _SEND_KEY(pCI, WM_KEYUP, VK_LSHIFT,
  3432. WM_KEY_LPARAM(1, 0x2A, 0, 0, 1, 1));
  3433. // Ctrl key
  3434. if (bCtrlKey)
  3435. _SEND_KEY(pCI, WM_KEYUP, VK_CONTROL,
  3436. WM_KEY_LPARAM(1, 0x1D, 0, 0, 1, 1));
  3437. #undef _SEND_KEY
  3438. exitpt:
  3439. return rv;
  3440. }
  3441. /*++
  3442. * Function:
  3443. * SwitchToProcess
  3444. * Description:
  3445. * Use Alt+Tab to switch to a particular process that is already running
  3446. * Arguments:
  3447. * pCI - connection context
  3448. * lpszParam - the text in the alt-tab box that uniquely identifies the
  3449. * process we should stop at (i.e., end up switching to)
  3450. * Return value:
  3451. * Error message, NULL on success
  3452. * Called by:
  3453. * SCCheck
  3454. --*/
  3455. PROTOCOLAPI
  3456. LPCSTR
  3457. SMCAPI
  3458. SCSwitchToProcess(PCONNECTINFO pCI, LPCWSTR lpszParam)
  3459. {
  3460. #define ALT_TAB_WAIT_TIMEOUT 1000
  3461. #define MAX_APPS 20
  3462. LPCSTR rv = NULL;
  3463. LPCSTR waitres = NULL;
  3464. INT retrys = MAX_APPS;
  3465. // WCHAR *wszCurrTask = 0;
  3466. if (!pCI)
  3467. {
  3468. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  3469. rv = ERR_NULL_CONNECTINFO;
  3470. goto exitpt;
  3471. }
  3472. if (pCI->dead)
  3473. {
  3474. rv = ERR_CLIENT_IS_DEAD;
  3475. goto exitpt;
  3476. }
  3477. // Wait and look for the string, before we do any switching. This makes
  3478. // sure we don't hit the string even before we hit alt-tab, and then
  3479. // end up switching to the wrong process
  3480. while (_Wait4Str(pCI, lpszParam, ALT_TAB_WAIT_TIMEOUT/5, WAIT_STRING) == 0)
  3481. ;
  3482. // Press alt down
  3483. SCSenddata(pCI, WM_KEYDOWN, 18, 540540929);
  3484. // Now loop through the list of applications (assuming there is one),
  3485. // stopping at our desired app.
  3486. do {
  3487. SCSenddata(pCI, WM_KEYDOWN, 9, 983041);
  3488. SCSenddata(pCI, WM_KEYUP, 9, -1072758783);
  3489. waitres = _Wait4Str(pCI, lpszParam, ALT_TAB_WAIT_TIMEOUT, WAIT_STRING);
  3490. retrys --;
  3491. } while (waitres && retrys);
  3492. SCSenddata(pCI, WM_KEYUP, 18, -1070071807);
  3493. rv = waitres;
  3494. exitpt:
  3495. return rv;
  3496. }
  3497. /*++
  3498. * Function:
  3499. * SCSetClientTopmost
  3500. * Description:
  3501. * Swithces the focus to this client
  3502. * Arguments:
  3503. * pCI - connection context
  3504. * lpszParam
  3505. * - "0" will remote the WS_EX_TOPMOST style
  3506. * - "non_zero" will set it as topmost window
  3507. * Return value:
  3508. * Error message, NULL on success
  3509. * Called by:
  3510. * SCCheck
  3511. --*/
  3512. PROTOCOLAPI
  3513. LPCSTR
  3514. SMCAPI
  3515. SCSetClientTopmost(
  3516. PCONNECTINFO pCI,
  3517. LPCWSTR lpszParam
  3518. )
  3519. {
  3520. LPCSTR rv = NULL;
  3521. BOOL bTop = FALSE;
  3522. HWND hClient;
  3523. if (!pCI)
  3524. {
  3525. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  3526. rv = ERR_NULL_CONNECTINFO;
  3527. goto exitpt;
  3528. }
  3529. if (pCI->dead)
  3530. {
  3531. rv = ERR_CLIENT_IS_DEAD;
  3532. goto exitpt;
  3533. }
  3534. #ifdef _RCLX
  3535. if (pCI->RClxMode)
  3536. {
  3537. TRACE((ERROR_MESSAGE, "SetClientOnFocus not supported in RCLX mode\n"));
  3538. rv = ERR_NOTSUPPORTED;
  3539. goto exitpt;
  3540. }
  3541. #endif // _RCLX
  3542. hClient = _FindTopWindow(pCI->pConfigInfo->strMainWindowClass,
  3543. NULL,
  3544. pCI->lProcessId);
  3545. if (!hClient)
  3546. {
  3547. TRACE((WARNING_MESSAGE, "Client's window handle is null\n"));
  3548. rv = ERR_INVALID_PARAM;
  3549. goto exitpt;
  3550. }
  3551. if (lpszParam)
  3552. bTop = (_wtoi(lpszParam) != 0);
  3553. else
  3554. bTop = 0;
  3555. if (!SetWindowPos(hClient,
  3556. (bTop)?HWND_TOPMOST:HWND_NOTOPMOST,
  3557. 0,0,0,0,
  3558. SWP_NOMOVE | SWP_NOSIZE))
  3559. {
  3560. TRACE(( ERROR_MESSAGE, "SetWindowPos failed=%d\n",
  3561. GetLastError()));
  3562. }
  3563. ShowWindow(hClient, SW_SHOWNORMAL);
  3564. if (bTop)
  3565. {
  3566. TRACE((INFO_MESSAGE, "Client is SET as topmost window\n"));
  3567. } else {
  3568. TRACE((INFO_MESSAGE, "Client is RESET as topmost window\n"));
  3569. }
  3570. exitpt:
  3571. return rv;
  3572. }
  3573. /*++
  3574. * Function:
  3575. * _SendMouseClick
  3576. * Description:
  3577. * Sends a messages for a mouse click
  3578. * Arguments:
  3579. * pCI - connection context
  3580. * xPos - mouse position
  3581. * yPos
  3582. * Return value:
  3583. * error string if fails, NULL on success
  3584. * Called by:
  3585. * * * * EXPORTED * * *
  3586. --*/
  3587. PROTOCOLAPI
  3588. LPCSTR
  3589. SMCAPI
  3590. SCSendMouseClick(
  3591. PCONNECTINFO pCI,
  3592. UINT xPos,
  3593. UINT yPos)
  3594. {
  3595. LPCSTR rv;
  3596. rv = SCSenddata(pCI, WM_LBUTTONDOWN, 0, xPos + (yPos << 16));
  3597. if (!rv)
  3598. SCSenddata(pCI, WM_LBUTTONUP, 0, xPos + (yPos << 16));
  3599. return rv;
  3600. }
  3601. #ifdef _RCLX
  3602. /*++
  3603. * Function:
  3604. * SCSaveClientScreen
  3605. * Description:
  3606. * Saves in a file rectangle of the client's receive screen buffer
  3607. * ( aka shadow bitmap)
  3608. * Arguments:
  3609. * pCI - connection context
  3610. * left, top, right, bottom - rectangle coordinates
  3611. * if all == -1 get's the whole screen
  3612. * szFileName - file to record
  3613. * Return value:
  3614. * error string if fails, NULL on success
  3615. * Called by:
  3616. * * * * EXPORTED * * *
  3617. --*/
  3618. PROTOCOLAPI
  3619. LPCSTR
  3620. SMCAPI
  3621. SCSaveClientScreen(
  3622. PCONNECTINFO pCI,
  3623. INT left,
  3624. INT top,
  3625. INT right,
  3626. INT bottom,
  3627. LPCSTR szFileName)
  3628. {
  3629. LPCSTR rv = NULL;
  3630. PVOID pDIB = NULL;
  3631. UINT uiSize = 0;
  3632. if (!szFileName)
  3633. {
  3634. TRACE((WARNING_MESSAGE, "SCSaveClientScreen: szFileName is NULL\n"));
  3635. rv = ERR_INVALID_PARAM;
  3636. goto exitpt;
  3637. }
  3638. // leave the rest of param checking to SCGetClientScreen
  3639. rv = SCGetClientScreen(pCI, left, top, right, bottom, &uiSize, &pDIB);
  3640. if (rv)
  3641. goto exitpt;
  3642. if (!pDIB || !uiSize)
  3643. {
  3644. TRACE((ERROR_MESSAGE, "SCSaveClientScreen: failed, no data\n"));
  3645. rv = ERR_NODATA;
  3646. goto exitpt;
  3647. }
  3648. if (!SaveDIB(pDIB, szFileName))
  3649. {
  3650. TRACE((ERROR_MESSAGE, "SCSaveClientScreen: save failed\n"));
  3651. rv = ERR_NODATA;
  3652. goto exitpt;
  3653. }
  3654. exitpt:
  3655. if (pDIB)
  3656. free(pDIB);
  3657. return rv;
  3658. }
  3659. /*++
  3660. * Function:
  3661. * SCGetClientScreen
  3662. * Description:
  3663. * Gets rectangle of the client's receive screen buffer
  3664. * ( aka shadow bitmap)
  3665. * Arguments:
  3666. * pCI - connection context
  3667. * left, top, right, bottom - rectangle coordinates
  3668. * if all == -1 get's the whole screen
  3669. * ppDIB - pointer to the received DIB
  3670. * puiSize - size of allocated data in ppDIB
  3671. *
  3672. * !!!!! DON'T FORGET to free() THAT MEMORY !!!!!
  3673. *
  3674. * Return value:
  3675. * error string if fails, NULL on success
  3676. * Called by:
  3677. * SCSaveClientScreen
  3678. * * * * EXPORTED * * *
  3679. --*/
  3680. PROTOCOLAPI
  3681. LPCSTR
  3682. SMCAPI
  3683. SCGetClientScreen(
  3684. PCONNECTINFO pCI,
  3685. INT left,
  3686. INT top,
  3687. INT right,
  3688. INT bottom,
  3689. UINT *puiSize,
  3690. PVOID *ppDIB)
  3691. {
  3692. LPCSTR rv;
  3693. PRCLXDATA pRClxData;
  3694. PREQBITMAP pReqBitmap;
  3695. PRCLXDATACHAIN pIter, pPrev, pNext;
  3696. PRCLXDATACHAIN pRClxDataChain = NULL;
  3697. if (!pCI)
  3698. {
  3699. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  3700. rv = ERR_NULL_CONNECTINFO;
  3701. goto exitpt;
  3702. }
  3703. if (pCI->dead)
  3704. {
  3705. rv = ERR_CLIENT_IS_DEAD;
  3706. goto exitpt;
  3707. }
  3708. if (!pCI->RClxMode)
  3709. {
  3710. TRACE((WARNING_MESSAGE, "SCGetClientScreen is not supported in non-RCLX mode\n"));
  3711. rv = ERR_NOTSUPPORTED;
  3712. goto exitpt;
  3713. }
  3714. if (!ppDIB || !puiSize)
  3715. {
  3716. TRACE((WARNING_MESSAGE, "ppDIB and/or puiSize parameter is NULL\n"));
  3717. rv = ERR_INVALID_PARAM;
  3718. goto exitpt;
  3719. }
  3720. // Remove all recieved DATA_BITMAP from the recieve buffer
  3721. EnterCriticalSection(g_lpcsGuardWaitQueue);
  3722. {
  3723. pIter = pCI->pRClxDataChain;
  3724. pPrev = NULL;
  3725. while (pIter)
  3726. {
  3727. pNext = pIter->pNext;
  3728. if (pIter->RClxData.uiType == DATA_BITMAP)
  3729. {
  3730. // dispose this entry
  3731. if (pPrev)
  3732. pPrev->pNext = pIter->pNext;
  3733. else
  3734. pCI->pRClxDataChain = pIter->pNext;
  3735. if (!pIter->pNext)
  3736. pCI->pRClxLastDataChain = pPrev;
  3737. free(pIter);
  3738. } else
  3739. pPrev = pIter;
  3740. pIter = pNext;
  3741. }
  3742. }
  3743. LeaveCriticalSection(g_lpcsGuardWaitQueue);
  3744. __try {
  3745. pRClxData = (PRCLXDATA) alloca(sizeof(*pRClxData) + sizeof(*pReqBitmap));
  3746. } __except (EXCEPTION_EXECUTE_HANDLER)
  3747. {
  3748. pRClxData = NULL;
  3749. }
  3750. if ( NULL == pRClxData )
  3751. {
  3752. goto exitpt;
  3753. }
  3754. pRClxData->uiType = DATA_BITMAP;
  3755. pRClxData->uiSize = sizeof(*pReqBitmap);
  3756. pReqBitmap = (PREQBITMAP)pRClxData->Data;
  3757. pReqBitmap->left = left;
  3758. pReqBitmap->top = top;
  3759. pReqBitmap->right = right;
  3760. pReqBitmap->bottom = bottom;
  3761. TRACE((INFO_MESSAGE, "Getting client's DIB (%d, %d, %d, %d)\n", left, top, right, bottom));
  3762. rv = _SendRClxData(pCI, pRClxData);
  3763. if (rv)
  3764. goto exitpt;
  3765. do {
  3766. rv = _Wait4RClxDataTimeout(pCI, pCI->pConfigInfo->WAIT4STR_TIMEOUT);
  3767. if (rv)
  3768. goto exitpt;
  3769. if (!pCI->pRClxDataChain)
  3770. {
  3771. TRACE((ERROR_MESSAGE, "RClxData is not received\n"));
  3772. rv = ERR_WAIT_FAIL_TIMEOUT;
  3773. goto exitpt;
  3774. }
  3775. EnterCriticalSection(g_lpcsGuardWaitQueue);
  3776. // Get any received DATA_BITMAP
  3777. {
  3778. pIter = pCI->pRClxDataChain;
  3779. pPrev = NULL;
  3780. while (pIter)
  3781. {
  3782. pNext = pIter->pNext;
  3783. if (pIter->RClxData.uiType == DATA_BITMAP)
  3784. {
  3785. // dispose this entry from the chain
  3786. if (pPrev)
  3787. pPrev->pNext = pIter->pNext;
  3788. else
  3789. pCI->pRClxDataChain = pIter->pNext;
  3790. if (!pIter->pNext)
  3791. pCI->pRClxLastDataChain = pPrev;
  3792. goto entry_is_found;
  3793. } else
  3794. pPrev = pIter;
  3795. pIter = pNext;
  3796. }
  3797. entry_is_found:
  3798. pRClxDataChain = (pIter && pIter->RClxData.uiType == DATA_BITMAP)?
  3799. pIter:NULL;
  3800. }
  3801. LeaveCriticalSection(g_lpcsGuardWaitQueue);
  3802. } while (!pRClxDataChain && !pCI->dead);
  3803. if (!pRClxDataChain)
  3804. {
  3805. TRACE((WARNING_MESSAGE, "SCGetClientScreen: client died\n"));
  3806. goto exitpt;
  3807. }
  3808. *ppDIB = malloc(pRClxDataChain->RClxData.uiSize);
  3809. if (!(*ppDIB))
  3810. {
  3811. TRACE((WARNING_MESSAGE, "Can't allocate %d bytes\n",
  3812. pRClxDataChain->RClxData.uiSize));
  3813. rv = ERR_ALLOCATING_MEMORY;
  3814. goto exitpt;
  3815. }
  3816. memcpy(*ppDIB,
  3817. pRClxDataChain->RClxData.Data,
  3818. pRClxDataChain->RClxData.uiSize);
  3819. *puiSize = pRClxDataChain->RClxData.uiSize;
  3820. exitpt:
  3821. if (pRClxDataChain)
  3822. free(pRClxDataChain);
  3823. return rv;
  3824. }
  3825. /*++
  3826. * Function:
  3827. * SCSendVCData
  3828. * Description:
  3829. * Sends data to a virtual channel
  3830. * Arguments:
  3831. * pCI - connection context
  3832. * szVCName - the virtual channel name
  3833. * pData - data
  3834. * uiSize - data size
  3835. * Return value:
  3836. * error string if fails, NULL on success
  3837. * Called by:
  3838. * * * * EXPORTED * * *
  3839. --*/
  3840. PROTOCOLAPI
  3841. LPCSTR
  3842. SMCAPI
  3843. SCSendVCData(
  3844. PCONNECTINFO pCI,
  3845. LPCSTR szVCName,
  3846. PVOID pData,
  3847. UINT uiSize
  3848. )
  3849. {
  3850. LPCSTR rv;
  3851. PRCLXDATA pRClxData = NULL;
  3852. CHAR *szName2Send;
  3853. PVOID pData2Send;
  3854. UINT uiPacketSize;
  3855. if (!pCI)
  3856. {
  3857. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  3858. rv = ERR_NULL_CONNECTINFO;
  3859. goto exitpt;
  3860. }
  3861. if (pCI->dead)
  3862. {
  3863. rv = ERR_CLIENT_IS_DEAD;
  3864. goto exitpt;
  3865. }
  3866. if (!pCI->RClxMode)
  3867. {
  3868. TRACE((WARNING_MESSAGE, "SCSendVCData is not supported in non-RCLXmode\n"));
  3869. rv = ERR_NOTSUPPORTED;
  3870. goto exitpt;
  3871. }
  3872. if (!pData || !uiSize)
  3873. {
  3874. TRACE((WARNING_MESSAGE, "pData and/or uiSize parameter are NULL\n"));
  3875. rv = ERR_INVALID_PARAM;
  3876. goto exitpt;
  3877. }
  3878. if (strlen(szVCName) > MAX_VCNAME_LEN - 1)
  3879. {
  3880. TRACE((WARNING_MESSAGE, "channel name too long\n"));
  3881. rv = ERR_INVALID_PARAM;
  3882. goto exitpt;
  3883. }
  3884. uiPacketSize = sizeof(*pRClxData) + MAX_VCNAME_LEN + uiSize;
  3885. pRClxData = (PRCLXDATA) malloc(uiPacketSize);
  3886. if (!pRClxData)
  3887. {
  3888. TRACE((ERROR_MESSAGE, "SCSendVCData: can't allocate %d bytes\n",
  3889. uiPacketSize));
  3890. rv = ERR_ALLOCATING_MEMORY;
  3891. goto exitpt;
  3892. }
  3893. pRClxData->uiType = DATA_VC;
  3894. pRClxData->uiSize = uiPacketSize - sizeof(*pRClxData);
  3895. szName2Send = (CHAR *)pRClxData->Data;
  3896. strcpy(szName2Send, szVCName);
  3897. pData2Send = szName2Send + MAX_VCNAME_LEN;
  3898. memcpy(pData2Send, pData, uiSize);
  3899. rv = _SendRClxData(pCI, pRClxData);
  3900. exitpt:
  3901. if (pRClxData)
  3902. free(pRClxData);
  3903. return rv;
  3904. }
  3905. /*++
  3906. * Function:
  3907. * SCRecvVCData
  3908. * Description:
  3909. * Receives data from virtual channel
  3910. * Arguments:
  3911. * pCI - connection context
  3912. * szVCName - the virtual channel name
  3913. * ppData - data pointer
  3914. *
  3915. * !!!!! DON'T FORGET to free() THAT MEMORY !!!!!
  3916. *
  3917. * puiSize - pointer to the data size
  3918. * Return value:
  3919. * error string if fails, NULL on success
  3920. * Called by:
  3921. * * * * EXPORTED * * *
  3922. --*/
  3923. PROTOCOLAPI
  3924. LPCSTR
  3925. SMCAPI
  3926. SCRecvVCData(
  3927. PCONNECTINFO pCI,
  3928. LPCSTR szVCName,
  3929. PVOID pData,
  3930. UINT uiBlockSize,
  3931. UINT *puiBytesRead
  3932. )
  3933. {
  3934. LPCSTR rv;
  3935. LPSTR szRecvVCName;
  3936. PVOID pChanData;
  3937. PRCLXDATACHAIN pIter, pPrev, pNext;
  3938. PRCLXDATACHAIN pRClxDataChain = NULL;
  3939. UINT uiBytesRead = 0;
  3940. BOOL bBlockFree = FALSE;
  3941. if (!pCI)
  3942. {
  3943. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  3944. rv = ERR_NULL_CONNECTINFO;
  3945. goto exitpt;
  3946. }
  3947. if (pCI->dead)
  3948. {
  3949. rv = ERR_CLIENT_IS_DEAD;
  3950. goto exitpt;
  3951. }
  3952. if (!pCI->RClxMode)
  3953. {
  3954. TRACE((WARNING_MESSAGE, "SCRecvVCData is not supported in non-RCLXmode\n"));
  3955. rv = ERR_NOTSUPPORTED;
  3956. goto exitpt;
  3957. }
  3958. if (!pData || !uiBlockSize || !puiBytesRead)
  3959. {
  3960. TRACE((WARNING_MESSAGE, "Invalid parameters\n"));
  3961. rv = ERR_INVALID_PARAM;
  3962. goto exitpt;
  3963. }
  3964. if (strlen(szVCName) > MAX_VCNAME_LEN - 1)
  3965. {
  3966. TRACE((WARNING_MESSAGE, "channel name too long\n"));
  3967. rv = ERR_INVALID_PARAM;
  3968. goto exitpt;
  3969. }
  3970. // Extract data entry from this channel
  3971. do {
  3972. if (!pCI->pRClxDataChain)
  3973. {
  3974. rv = _Wait4RClxDataTimeout(pCI, pCI->pConfigInfo->WAIT4STR_TIMEOUT);
  3975. if (rv)
  3976. goto exitpt;
  3977. }
  3978. EnterCriticalSection(g_lpcsGuardWaitQueue);
  3979. // Search for data from this channel
  3980. {
  3981. pIter = pCI->pRClxDataChain;
  3982. pPrev = NULL;
  3983. while (pIter)
  3984. {
  3985. pNext = pIter->pNext;
  3986. if (pIter->RClxData.uiType == DATA_VC &&
  3987. !_stricmp((LPCSTR) pIter->RClxData.Data, szVCName))
  3988. {
  3989. if (pIter->RClxData.uiSize - pIter->uiOffset - MAX_VCNAME_LEN <= uiBlockSize)
  3990. {
  3991. // will read the whole block
  3992. // dispose this entry
  3993. if (pPrev)
  3994. pPrev->pNext = pIter->pNext;
  3995. else
  3996. pCI->pRClxDataChain = pIter->pNext;
  3997. if (!pIter->pNext)
  3998. pCI->pRClxLastDataChain = pPrev;
  3999. bBlockFree = TRUE;
  4000. }
  4001. goto entry_is_found;
  4002. } else
  4003. pPrev = pIter;
  4004. pIter = pNext;
  4005. }
  4006. entry_is_found:
  4007. pRClxDataChain = (pIter && pIter->RClxData.uiType == DATA_VC)?
  4008. pIter:NULL;
  4009. }
  4010. LeaveCriticalSection(g_lpcsGuardWaitQueue);
  4011. } while (!pRClxDataChain && !pCI->dead);
  4012. ASSERT(pRClxDataChain->RClxData.uiType == DATA_VC);
  4013. szRecvVCName = (LPSTR) pRClxDataChain->RClxData.Data;
  4014. if (_stricmp(szRecvVCName, szVCName))
  4015. {
  4016. TRACE((ERROR_MESSAGE, "SCRecvVCData: received from different channel: %s\n", szRecvVCName));
  4017. ASSERT(0);
  4018. }
  4019. pChanData = (BYTE *)(pRClxDataChain->RClxData.Data) +
  4020. pRClxDataChain->uiOffset + MAX_VCNAME_LEN;
  4021. uiBytesRead = pRClxDataChain->RClxData.uiSize -
  4022. pRClxDataChain->uiOffset - MAX_VCNAME_LEN;
  4023. if (uiBytesRead > uiBlockSize)
  4024. uiBytesRead = uiBlockSize;
  4025. memcpy(pData, pChanData, uiBytesRead);
  4026. pRClxDataChain->uiOffset += uiBytesRead;
  4027. rv = NULL;
  4028. exitpt:
  4029. if (pRClxDataChain && bBlockFree)
  4030. {
  4031. ASSERT(pRClxDataChain->uiOffset + MAX_VCNAME_LEN == pRClxDataChain->RClxData.uiSize);
  4032. free(pRClxDataChain);
  4033. }
  4034. if (puiBytesRead)
  4035. {
  4036. *puiBytesRead = uiBytesRead;
  4037. TRACE((INFO_MESSAGE, "SCRecvVCData: %d bytes read\n", uiBytesRead));
  4038. }
  4039. return rv;
  4040. }
  4041. #endif // _RCLX
  4042. /*++
  4043. * Function:
  4044. * _EnumWindowsProc
  4045. * Description:
  4046. * Used to find a specific window
  4047. * Arguments:
  4048. * hWnd - current enumerated window handle
  4049. * lParam - pointer to SEARCHWND passed from
  4050. * _FindTopWindow
  4051. * Return value:
  4052. * TRUE on success but window is not found
  4053. * FALSE if the window is found
  4054. * Called by:
  4055. * _FindTopWindow thru EnumWindows
  4056. --*/
  4057. BOOL CALLBACK _EnumWindowsProc( HWND hWnd, LPARAM lParam )
  4058. {
  4059. TCHAR classname[128];
  4060. TCHAR caption[128];
  4061. BOOL rv = TRUE;
  4062. DWORD dwProcessId;
  4063. LONG_PTR lProcessId;
  4064. PSEARCHWND pSearch = (PSEARCHWND)lParam;
  4065. if (pSearch->szClassName &&
  4066. // !GetClassNameWrp(hWnd, classname, sizeof(classname)/sizeof(classname[0])))
  4067. !GetClassNameW(hWnd, classname, sizeof(classname)/sizeof(classname[0])))
  4068. {
  4069. goto exitpt;
  4070. }
  4071. if (pSearch->szCaption &&
  4072. // !GetWindowTextWrp(hWnd, caption, sizeof(caption)/sizeof(caption[0])))
  4073. !GetWindowTextW(hWnd, caption, sizeof(caption)/sizeof(caption[0])))
  4074. {
  4075. goto exitpt;
  4076. }
  4077. GetWindowThreadProcessId(hWnd, &dwProcessId);
  4078. lProcessId = dwProcessId;
  4079. if (
  4080. (!pSearch->szClassName || ! // Check for classname
  4081. #ifdef UNICODE
  4082. wcscmp
  4083. #else
  4084. strcmp
  4085. #endif
  4086. (classname, pSearch->szClassName))
  4087. &&
  4088. (!pSearch->szCaption || !
  4089. #ifdef UNICODE
  4090. wcscmp
  4091. #else
  4092. strcmp
  4093. #endif
  4094. (caption, pSearch->szCaption))
  4095. &&
  4096. lProcessId == pSearch->lProcessId)
  4097. {
  4098. ((PSEARCHWND)lParam)->hWnd = hWnd;
  4099. rv = FALSE;
  4100. }
  4101. exitpt:
  4102. return rv;
  4103. }
  4104. /*++
  4105. * Function:
  4106. * _FindTopWindow
  4107. * Description:
  4108. * Find specific window by classname and/or caption and/or process Id
  4109. * Arguments:
  4110. * classname - class name to search for, NULL ignore
  4111. * caption - caption to search for, NULL ignore
  4112. * dwProcessId - process Id, 0 ignore
  4113. * Return value:
  4114. * window handle found, NULL otherwise
  4115. * Called by:
  4116. * SCConnect, SCDisconnect, GetDisconnectResult
  4117. --*/
  4118. HWND _FindTopWindow(LPTSTR classname, LPTSTR caption, LONG_PTR lProcessId)
  4119. {
  4120. SEARCHWND search;
  4121. search.szClassName = classname;
  4122. search.szCaption = caption;
  4123. search.hWnd = NULL;
  4124. search.lProcessId = lProcessId;
  4125. EnumWindows(_EnumWindowsProc, (LPARAM)&search);
  4126. return search.hWnd;
  4127. }
  4128. /*++
  4129. * Function:
  4130. * _FindWindow
  4131. * Description:
  4132. * Find child window by caption and/or classname
  4133. * Arguments:
  4134. * hwndParent - the parent window handle
  4135. * srchcaption - caption to search for, NULL - ignore
  4136. * srchclass - class name to search for, NULL - ignore
  4137. * Return value:
  4138. * window handle found, NULL otherwise
  4139. * Called by:
  4140. * SCConnect
  4141. --*/
  4142. HWND _FindWindow(HWND hwndParent, LPTSTR srchcaption, LPTSTR srchclass)
  4143. {
  4144. HWND hWnd, hwndTop, hwndNext;
  4145. BOOL bFound;
  4146. TCHAR classname[128];
  4147. TCHAR caption[128];
  4148. hWnd = NULL;
  4149. hwndTop = GetWindow(hwndParent, GW_CHILD);
  4150. if (!hwndTop)
  4151. {
  4152. TRACE((INFO_MESSAGE, "GetWindow failed. hwnd=0x%x\n", hwndParent));
  4153. goto exiterr;
  4154. }
  4155. bFound = FALSE;
  4156. hwndNext = hwndTop;
  4157. do {
  4158. hWnd = hwndNext;
  4159. // if (srchclass && !GetClassNameWrp(hWnd, classname, sizeof(classname)))
  4160. if (srchclass && !GetClassNameW(hWnd, classname, sizeof(classname)/sizeof(classname[0])))
  4161. {
  4162. TRACE((INFO_MESSAGE, "GetClassName failed. hwnd=0x%x\n"));
  4163. goto nextwindow;
  4164. }
  4165. if (srchcaption &&
  4166. // !GetWindowTextWrp(hWnd, caption, sizeof(caption)/sizeof(classname[0])))
  4167. !GetWindowTextW(hWnd, caption, sizeof(caption)/sizeof(classname[0])/sizeof(classname[0])))
  4168. {
  4169. TRACE((INFO_MESSAGE, "GetWindowText failed. hwnd=0x%x\n"));
  4170. goto nextwindow;
  4171. }
  4172. if (
  4173. (!srchclass || !
  4174. #ifdef UNICODE
  4175. wcscmp
  4176. #else
  4177. strcmp
  4178. #endif
  4179. (classname, srchclass))
  4180. &&
  4181. (!srchcaption || !
  4182. #ifdef UNICODE
  4183. wcscmp
  4184. #else
  4185. strcmp
  4186. #endif
  4187. (caption, srchcaption))
  4188. )
  4189. bFound = TRUE;
  4190. else {
  4191. //
  4192. // search recursively
  4193. //
  4194. HWND hSubWnd = _FindWindow( hWnd, srchcaption, srchclass);
  4195. if ( NULL != hSubWnd )
  4196. {
  4197. bFound = TRUE;
  4198. hWnd = hSubWnd;
  4199. goto exitpt;
  4200. }
  4201. }
  4202. nextwindow:
  4203. hwndNext = GetNextWindow(hWnd, GW_HWNDNEXT);
  4204. } while (hWnd && hwndNext != hwndTop && !bFound);
  4205. exitpt:
  4206. if (!bFound) goto exiterr;
  4207. return hWnd;
  4208. exiterr:
  4209. return NULL;
  4210. }
  4211. BOOL
  4212. _IsExtendedScanCode(INT scancode)
  4213. {
  4214. static BYTE extscans[] = \
  4215. {28, 29, 53, 55, 56, 71, 72, 73, 75, 77, 79, 80, 81, 82, 83, 87, 88};
  4216. INT idx;
  4217. for (idx = 0; idx < sizeof(extscans); idx++)
  4218. {
  4219. if (scancode == (INT)extscans[idx])
  4220. return TRUE;
  4221. }
  4222. return FALSE;
  4223. }
  4224. PROTOCOLAPI
  4225. BOOL
  4226. SMCAPI
  4227. SCOpenClipboard(HWND hwnd)
  4228. {
  4229. return OpenClipboard(hwnd);
  4230. }
  4231. PROTOCOLAPI
  4232. BOOL
  4233. SMCAPI
  4234. SCCloseClipboard(VOID)
  4235. {
  4236. return CloseClipboard();
  4237. }
  4238. PROTOCOLAPI
  4239. LPCSTR
  4240. SMCAPI
  4241. SCDetach(
  4242. PCONNECTINFO pCI
  4243. )
  4244. {
  4245. LPCSTR rv = NULL;
  4246. if ( NULL == pCI )
  4247. {
  4248. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  4249. rv = ERR_NULL_CONNECTINFO;
  4250. goto exitpt;
  4251. }
  4252. if (!_RemoveFromClientQ(pCI))
  4253. {
  4254. TRACE(( WARNING_MESSAGE,
  4255. "Couldn't find CONNECTINFO in the queue\n" ));
  4256. }
  4257. _CloseConnectInfo( pCI );
  4258. exitpt:
  4259. return rv;
  4260. }
  4261. /*++
  4262. * Function:
  4263. * SCAttach
  4264. * Description:
  4265. * Attach CONNECTINFO to a client window, assuming that the client
  4266. * is already started
  4267. * it uses a special cookie to identify the client in the future
  4268. * It is recommended to call SCDetach instead of SCLogoff or SCDisconnect
  4269. * Arguments:
  4270. * hClient - handle to a container window
  4271. * the function will find the client window in the child windows
  4272. * lClientCookie - This value is used to identify the client
  4273. * in normal SCConnect function the client's process id is used
  4274. * here any value could be used, but the client has to be notified for
  4275. * it
  4276. * ppCI - the function returns non-NULL connection structure on
  4277. * success
  4278. * Return value:
  4279. * SC error message
  4280. * Called by:
  4281. * exported
  4282. --*/
  4283. PROTOCOLAPI
  4284. LPCSTR
  4285. SMCAPI
  4286. SCAttach(
  4287. HWND hClient,
  4288. LONG_PTR lClientCookie,
  4289. PCONNECTINFO *ppCI
  4290. )
  4291. {
  4292. LPCSTR rv = NULL;
  4293. PCONNECTINFO pCI = NULL;
  4294. HWND hContainer = NULL;
  4295. HWND hInput = NULL;
  4296. HWND hOutput = NULL;
  4297. UINT trys;
  4298. pCI = (PCONNECTINFO) malloc( sizeof( *pCI ));
  4299. if ( NULL == pCI )
  4300. {
  4301. TRACE(( ERROR_MESSAGE, "SCAttach: failed to allocate memory\n" ));
  4302. rv = ERR_ALLOCATING_MEMORY;
  4303. goto exitpt;
  4304. }
  4305. ZeroMemory( pCI, sizeof( *pCI ));
  4306. //
  4307. // get all the windows we need
  4308. //
  4309. trys = 240; // 2 min
  4310. do {
  4311. hContainer = _FindWindow(hClient, NULL, NAME_CONTAINERCLASS);
  4312. hInput = _FindWindow(hContainer, NULL, NAME_INPUT);
  4313. hOutput = _FindWindow(hContainer, NULL, NAME_OUTPUT);
  4314. if (!hContainer || !hInput || !hOutput)
  4315. {
  4316. TRACE((INFO_MESSAGE, "Can't get child windows. Retry"));
  4317. Sleep(500);
  4318. trys--;
  4319. }
  4320. } while ((!hContainer || !hInput || !hOutput) && trys);
  4321. if (!trys)
  4322. {
  4323. TRACE((WARNING_MESSAGE, "Can't find child windows"));
  4324. rv = ERR_CONNECTING;
  4325. goto exitpt;
  4326. }
  4327. TRACE((INFO_MESSAGE, "hClient = 0x%x\n", hClient));
  4328. TRACE((INFO_MESSAGE, "hContainer= 0x%x\n", hContainer));
  4329. TRACE((INFO_MESSAGE, "hInput = 0x%x\n", hInput));
  4330. TRACE((INFO_MESSAGE, "hOutput = 0x%x\n", hOutput));
  4331. TRACE((INFO_MESSAGE, "ClientCookie= 0x%x\n", lClientCookie ));
  4332. pCI->hClient = hClient;
  4333. pCI->hContainer = hContainer;
  4334. pCI->hInput = hInput;
  4335. pCI->hOutput = hOutput;
  4336. pCI->lProcessId = lClientCookie;
  4337. *ppCI = pCI;
  4338. _AddToClientQ(*ppCI);
  4339. //
  4340. // success !!!
  4341. //
  4342. exitpt:
  4343. if ( NULL != rv && NULL != pCI )
  4344. {
  4345. SCDetach( pCI );
  4346. *ppCI = NULL;
  4347. }
  4348. return rv;
  4349. }
  4350. /*++
  4351. * Function:
  4352. * _IsSmartcardActive
  4353. * Description:
  4354. * Determine whether or not to look for the smartcard UI.
  4355. * Arguments:
  4356. * None.
  4357. * Return value:
  4358. * TRUE if the smartcard UI is expected, FALSE otherwise.
  4359. * Called by:
  4360. * _Login
  4361. * Author:
  4362. * Based on code from Sermet Iskin (sermeti) 15-Jan-2002
  4363. * Alex Stephens (alexstep) 20-Jan-2002
  4364. --*/
  4365. BOOL
  4366. _IsSmartcardActive(
  4367. VOID
  4368. )
  4369. {
  4370. SCARDCONTEXT hCtx;
  4371. LPCTSTR mszRdrs;
  4372. LPCTSTR szRdr;
  4373. DWORD cchRdrs;
  4374. DWORD dwRet;
  4375. DWORD cRdrs;
  4376. SCARD_READERSTATE rgStateArr[MAXIMUM_SMARTCARD_READERS];
  4377. DWORD dwIndex;
  4378. BOOL fSuccess;
  4379. //
  4380. // Windows releases earlier than XP (NT 5.1/2600) do not support
  4381. // smartcards, so return if running on such.
  4382. //
  4383. if (!ISSMARTCARDAWARE())
  4384. {
  4385. TRACE((INFO_MESSAGE, "OS does not support smartcards.\n"));
  4386. return FALSE;
  4387. }
  4388. //
  4389. // Load the smartcard library, which will set the appropriate function
  4390. // pointers. It is loaded only once, and remains loaded until the process
  4391. // exits.
  4392. //
  4393. dwRet = _LoadSmartcardLibrary();
  4394. if (dwRet != ERROR_SUCCESS)
  4395. {
  4396. TRACE((ERROR_MESSAGE,
  4397. "Unable to load smartcard library (error %#x).\n",
  4398. dwRet));
  4399. return FALSE;
  4400. }
  4401. ASSERT(g_hSmartcardLibrary != NULL);
  4402. //
  4403. // Get an scard context. If this fails, the service might not be running.
  4404. //
  4405. ASSERT(g_pfnSCardEstablishContext != NULL);
  4406. dwRet = g_pfnSCardEstablishContext(SCARD_SCOPE_SYSTEM,
  4407. NULL,
  4408. NULL,
  4409. &hCtx);
  4410. switch (dwRet)
  4411. {
  4412. //
  4413. // Got scard context.
  4414. //
  4415. case SCARD_S_SUCCESS:
  4416. TRACE((INFO_MESSAGE, "Smartcard context established.\n"));
  4417. break;
  4418. //
  4419. // The smartcard service is not running, so there will be no
  4420. // smartcard UI.
  4421. //
  4422. case SCARD_E_NO_SERVICE:
  4423. TRACE((INFO_MESSAGE, "Smartcard service not running.\n"));
  4424. return FALSE;
  4425. break;
  4426. //
  4427. // The call has failed.
  4428. //
  4429. default:
  4430. TRACE((ERROR_MESSAGE,
  4431. "Unable to establish smartcard context (error %#x).\n",
  4432. dwRet));
  4433. return FALSE;
  4434. break;
  4435. }
  4436. ASSERT(hCtx != 0);
  4437. //
  4438. // Always release the smartcard context.
  4439. //
  4440. fSuccess = FALSE;
  4441. try
  4442. {
  4443. //
  4444. // Get the list of the readers, using an auto-allocated buffer.
  4445. //
  4446. mszRdrs = NULL;
  4447. cchRdrs = SCARD_AUTOALLOCATE;
  4448. ASSERT(g_pfnSCardListReaders != NULL);
  4449. dwRet = g_pfnSCardListReaders(hCtx,
  4450. NULL,
  4451. (LPTSTR)&mszRdrs,
  4452. &cchRdrs);
  4453. switch (dwRet)
  4454. {
  4455. //
  4456. // Readers are present.
  4457. //
  4458. case SCARD_S_SUCCESS:
  4459. ASSERT(cchRdrs != 0 &&
  4460. mszRdrs != NULL &&
  4461. *mszRdrs != TEXT('\0'));
  4462. TRACE((INFO_MESSAGE, "Smartcard readers are present.\n"));
  4463. break;
  4464. //
  4465. // No readers are present, so there will be no smartcard UI.
  4466. //
  4467. case SCARD_E_NO_READERS_AVAILABLE:
  4468. TRACE((INFO_MESSAGE, "No smartcard readers are present.\n"));
  4469. leave;
  4470. break;
  4471. //
  4472. // The call has failed.
  4473. //
  4474. default:
  4475. TRACE((ERROR_MESSAGE,
  4476. "Unable to get smartcard-reader list (error %#x).\n",
  4477. dwRet));
  4478. leave;
  4479. break;
  4480. }
  4481. //
  4482. // Always free the reader-list buffer, which is allocated by the
  4483. // smartcard code.
  4484. //
  4485. try
  4486. {
  4487. //
  4488. // Count the number of readers.
  4489. //
  4490. ZeroMemory(rgStateArr, sizeof(rgStateArr));
  4491. for (szRdr = _FirstString(mszRdrs), cRdrs = 0;
  4492. szRdr != NULL;
  4493. szRdr = _NextString(szRdr))
  4494. {
  4495. rgStateArr[cRdrs].szReader = szRdr;
  4496. rgStateArr[cRdrs].dwCurrentState = SCARD_STATE_UNAWARE;
  4497. cRdrs += 1;
  4498. }
  4499. //
  4500. // Query for reader states.
  4501. //
  4502. ASSERT(g_pfnSCardGetStatusChange != NULL);
  4503. dwRet = g_pfnSCardGetStatusChange(hCtx, 0, rgStateArr, cRdrs);
  4504. if (dwRet != SCARD_S_SUCCESS)
  4505. {
  4506. TRACE((
  4507. ERROR_MESSAGE,
  4508. "Unable to query smartcard-reader states (error %#x).\n",
  4509. dwRet));
  4510. leave;
  4511. }
  4512. //
  4513. // Check each reader for a card. If one is found, the smartcard
  4514. // UI must be handled.
  4515. //
  4516. for (dwIndex = 0; dwIndex < cRdrs; dwIndex += 1)
  4517. {
  4518. if (rgStateArr[dwIndex].dwEventState & SCARD_STATE_PRESENT)
  4519. {
  4520. TRACE((INFO_MESSAGE, "Smartcard present.\n"));
  4521. fSuccess = TRUE;
  4522. leave;
  4523. }
  4524. }
  4525. //
  4526. // No smartcards were found, so there will be no smartcard UI.
  4527. //
  4528. TRACE((INFO_MESSAGE, "No smartcards are present.\n"));
  4529. }
  4530. //
  4531. // Free reader strings.
  4532. //
  4533. finally
  4534. {
  4535. ASSERT(g_pfnSCardFreeMemory != NULL);
  4536. ASSERT(hCtx != 0);
  4537. ASSERT(mszRdrs != NULL);
  4538. g_pfnSCardFreeMemory(hCtx, mszRdrs);
  4539. mszRdrs = NULL;
  4540. }
  4541. }
  4542. //
  4543. // Release smartcard context.
  4544. //
  4545. finally
  4546. {
  4547. ASSERT(g_pfnSCardReleaseContext != NULL);
  4548. ASSERT(hCtx != 0);
  4549. g_pfnSCardReleaseContext(hCtx);
  4550. hCtx = 0;
  4551. }
  4552. return fSuccess;
  4553. }
  4554. /*++
  4555. * Function:
  4556. * _LoadSmartcardLibrary
  4557. * Description:
  4558. * This routine loads the smartcard library.
  4559. * Arguments:
  4560. * None.
  4561. * Return value:
  4562. * ERROR_SUCCESS if successful, an appropriate Win32 error code
  4563. * otherwise.
  4564. * Called by:
  4565. * _IsSmartcardActive
  4566. * Author:
  4567. * Alex Stephens (alexstep) 20-Jan-2002
  4568. --*/
  4569. DWORD
  4570. _LoadSmartcardLibrary(
  4571. VOID
  4572. )
  4573. {
  4574. HANDLE hSmartcardLibrary;
  4575. HANDLE hPreviousSmartcardLibrary;
  4576. //
  4577. // If the smartcard library has already been loaded, succeed.
  4578. //
  4579. if (g_hSmartcardLibrary != NULL &&
  4580. g_pfnSCardEstablishContext != NULL &&
  4581. g_pfnSCardListReaders != NULL &&
  4582. g_pfnSCardGetStatusChange != NULL &&
  4583. g_pfnSCardFreeMemory != NULL &&
  4584. g_pfnSCardReleaseContext != NULL)
  4585. {
  4586. return ERROR_SUCCESS;
  4587. }
  4588. //
  4589. // Load the library.
  4590. //
  4591. hSmartcardLibrary = LoadLibrary(SMARTCARD_LIBRARY);
  4592. if (hSmartcardLibrary == NULL)
  4593. {
  4594. TRACE((ERROR_MESSAGE, "Unable to load smardcard library.\n"));
  4595. return GetLastError();
  4596. }
  4597. //
  4598. // Save the library handle to the global pointer. If it has already been
  4599. // set, decrement the reference count.
  4600. //
  4601. hPreviousSmartcardLibrary =
  4602. InterlockedExchangePointer(&g_hSmartcardLibrary,
  4603. hSmartcardLibrary);
  4604. if (hPreviousSmartcardLibrary != NULL)
  4605. {
  4606. RTL_VERIFY(FreeLibrary(hSmartcardLibrary));
  4607. }
  4608. //
  4609. // Get the addresses of the smartcard routines.
  4610. //
  4611. return _GetSmartcardRoutines();
  4612. }
  4613. /*++
  4614. * Function:
  4615. * _GetSmartcardRoutines
  4616. * Description:
  4617. * This routine sets the global function pointers used to call the
  4618. * smartcard routines.
  4619. * Arguments:
  4620. * None.
  4621. * Return value:
  4622. * ERROR_SUCCESS if successful, an appropriate Win32 error code
  4623. * otherwise.
  4624. * Called by:
  4625. * _LoadSmartcardLibrary
  4626. * Author:
  4627. * Alex Stephens (alexstep) 20-Jan-2002
  4628. --*/
  4629. DWORD
  4630. _GetSmartcardRoutines(
  4631. VOID
  4632. )
  4633. {
  4634. FARPROC pfnSCardEstablishContext;
  4635. FARPROC pfnSCardListReaders;
  4636. FARPROC pfnSCardGetStatusChange;
  4637. FARPROC pfnSCardFreeMemory;
  4638. FARPROC pfnSCardReleaseContext;
  4639. //
  4640. // If the smartcard pointers have already been set, succeed.
  4641. //
  4642. ASSERT(g_hSmartcardLibrary != NULL);
  4643. if (g_pfnSCardEstablishContext != NULL &&
  4644. g_pfnSCardListReaders != NULL &&
  4645. g_pfnSCardGetStatusChange != NULL &&
  4646. g_pfnSCardFreeMemory != NULL &&
  4647. g_pfnSCardReleaseContext != NULL)
  4648. {
  4649. return ERROR_SUCCESS;
  4650. }
  4651. //
  4652. // Get the address of each routine.
  4653. //
  4654. pfnSCardEstablishContext = GetProcAddress(g_hSmartcardLibrary,
  4655. SCARDESTABLISHCONTEXT);
  4656. if (pfnSCardEstablishContext == NULL)
  4657. {
  4658. TRACE((ERROR_MESSAGE,
  4659. "Unable to get SCardEstablishContext address.\n"));
  4660. return GetLastError();
  4661. }
  4662. pfnSCardListReaders = GetProcAddress(g_hSmartcardLibrary,
  4663. SCARDLISTREADERS);
  4664. if (pfnSCardListReaders == NULL)
  4665. {
  4666. TRACE((ERROR_MESSAGE, "Unable to get SCardListReaders address.\n"));
  4667. return GetLastError();
  4668. }
  4669. pfnSCardGetStatusChange = GetProcAddress(g_hSmartcardLibrary,
  4670. SCARDGETSTATUSCHANGE);
  4671. if (pfnSCardGetStatusChange == NULL)
  4672. {
  4673. TRACE((ERROR_MESSAGE,
  4674. "Unable to get SCardGetStatusChange address.\n"));
  4675. return GetLastError();
  4676. }
  4677. pfnSCardFreeMemory = GetProcAddress(g_hSmartcardLibrary,
  4678. SCARDFREEMEMORY);
  4679. if (pfnSCardFreeMemory == NULL)
  4680. {
  4681. TRACE((ERROR_MESSAGE, "Unable to get SCardFreeMemory address.\n"));
  4682. return GetLastError();
  4683. }
  4684. pfnSCardReleaseContext = GetProcAddress(g_hSmartcardLibrary,
  4685. SCARDRELEASECONTEXT);
  4686. if (pfnSCardReleaseContext == NULL)
  4687. {
  4688. TRACE((ERROR_MESSAGE,
  4689. "Unable to get SCardReleaseContext address.\n"));
  4690. return GetLastError();
  4691. }
  4692. //
  4693. // Fill in any the global pointers. It would be better to
  4694. // compare/exchange, but Windows 95 lacks the necessary APIs.
  4695. //
  4696. InterlockedExchangePointer((PVOID *)&g_pfnSCardEstablishContext,
  4697. pfnSCardEstablishContext);
  4698. ASSERT(g_pfnSCardEstablishContext != NULL);
  4699. InterlockedExchangePointer((PVOID *)&g_pfnSCardListReaders,
  4700. pfnSCardListReaders);
  4701. ASSERT(g_pfnSCardListReaders != NULL);
  4702. InterlockedExchangePointer((PVOID *)&g_pfnSCardGetStatusChange,
  4703. pfnSCardGetStatusChange);
  4704. ASSERT(g_pfnSCardGetStatusChange != NULL);
  4705. InterlockedExchangePointer((PVOID *)&g_pfnSCardFreeMemory,
  4706. pfnSCardFreeMemory);
  4707. ASSERT(g_pfnSCardFreeMemory != NULL);
  4708. InterlockedExchangePointer((PVOID *)&g_pfnSCardReleaseContext,
  4709. pfnSCardReleaseContext);
  4710. ASSERT(g_pfnSCardReleaseContext != NULL);
  4711. return ERROR_SUCCESS;
  4712. }
  4713. /*++
  4714. * Function:
  4715. * _FirstString
  4716. * Description:
  4717. * This routine returns a pointer to the first string in a multistring,
  4718. * or NULL if there aren't any.
  4719. * Arguments:
  4720. * szMultiString - This supplies the address of the current position
  4721. * within a Multi-string structure.
  4722. * Return value:
  4723. * The address of the first null-terminated string in the structure, or
  4724. * NULL if there are no strings.
  4725. * Called by:
  4726. * _IsSmartcardActive
  4727. * Author:
  4728. * Doug Barlow (dbarlow) 11/25/1996
  4729. * Alex Stephens (alexstep) 20-Jan-2002
  4730. --*/
  4731. LPCTSTR
  4732. _FirstString(
  4733. IN LPCTSTR szMultiString
  4734. )
  4735. {
  4736. //
  4737. // If the multi-string is NULL, or is empty, there is no first string.
  4738. //
  4739. if (szMultiString == NULL || *szMultiString == TEXT('\0'))
  4740. {
  4741. return NULL;
  4742. }
  4743. return szMultiString;
  4744. }
  4745. /*++
  4746. * Function:
  4747. * _NextString
  4748. * Description:
  4749. * In some cases, the Smartcard API returns multiple strings, separated
  4750. * by Null characters, and terminated by two null characters in a row.
  4751. * This routine simplifies access to such structures. Given the current
  4752. * string in a multi-string structure, it returns the next string, or
  4753. * NULL if no other strings follow the current string.
  4754. * Arguments:
  4755. * szMultiString - This supplies the address of the current position
  4756. * within a Multi-string structure.
  4757. * Return value:
  4758. * The address of the next Null-terminated string in the structure, or
  4759. * NULL if no more strings follow.
  4760. * Called by:
  4761. * _IsSmartcardActive
  4762. * Author:
  4763. * Doug Barlow (dbarlow) 8/12/1996
  4764. * Alex Stephens (alexstep) 20-Jan-2002
  4765. --*/
  4766. LPCTSTR
  4767. _NextString(
  4768. IN LPCTSTR szMultiString
  4769. )
  4770. {
  4771. DWORD_PTR dwLength;
  4772. LPCTSTR szNext;
  4773. //
  4774. // If the multi-string is NULL, or is empty, there is no next string.
  4775. //
  4776. if (szMultiString == NULL || *szMultiString == TEXT('\0'))
  4777. {
  4778. return NULL;
  4779. }
  4780. //
  4781. // Get the length of the current string.
  4782. //
  4783. dwLength = _tcslen(szMultiString);
  4784. ASSERT(dwLength != 0);
  4785. //
  4786. // Skip the current string, including the terminating NULL, and check to
  4787. // see if there is a next string.
  4788. //
  4789. szNext = szMultiString + dwLength + 1;
  4790. if (*szNext == TEXT('\0'))
  4791. {
  4792. return NULL;
  4793. }
  4794. return szNext;
  4795. }
  4796. /*++
  4797. * Function:
  4798. * _SendRunHotkey
  4799. * Description:
  4800. * This routine sends the Windows hotkey used to open the shell's Run
  4801. * window.
  4802. * Note: Keyboard hooks must be enabled for this to work!
  4803. * Arguments:
  4804. * pCI - Supplies the connection context.
  4805. * bFallBack - Supplies a value indicating whether or not to fall back
  4806. * to Ctrl+Esc and R if the keyboard hook is disabled.
  4807. * Return value:
  4808. * None.
  4809. * Called by:
  4810. * SCLogoff
  4811. * SCStart
  4812. * Author:
  4813. * Alex Stephens (alexstep) 15-Jan-2002
  4814. --*/
  4815. VOID
  4816. _SendRunHotkey(
  4817. IN CONST PCONNECTINFO pCI,
  4818. IN BOOL bFallBack
  4819. )
  4820. {
  4821. ASSERT(pCI != NULL);
  4822. //
  4823. // Send Win+R if the keyboard hook is enabled.
  4824. //
  4825. if (pCI->pConfigInfo->KeyboardHook == TCLIENT_KEYBOARD_HOOK_ALWAYS)
  4826. {
  4827. TRACE((INFO_MESSAGE, "Sending Win+R hotkey.\n"));
  4828. SCSenddata(pCI, WM_KEYDOWN, 0x0000005B, 0x015B0001);
  4829. SCSenddata(pCI, WM_KEYDOWN, 0x00000052, 0x00130001);
  4830. SCSenddata(pCI, WM_CHAR, 0x00000072, 0x00130001);
  4831. SCSenddata(pCI, WM_KEYUP, 0x00000052, 0x80130001);
  4832. SCSenddata(pCI, WM_KEYUP, 0x0000005B, 0x815B0001);
  4833. }
  4834. //
  4835. // If the keyboard hook is not enabled, either fail or try Ctrl+Esc and
  4836. // the Run key.
  4837. //
  4838. else
  4839. {
  4840. if (bFallBack)
  4841. {
  4842. TRACE((INFO_MESSAGE, "Sending Ctrl+Esc and Run key.\n"));
  4843. SCSenddata(pCI, WM_KEYDOWN, 0x00000011, 0x001D0001);
  4844. SCSenddata(pCI, WM_KEYDOWN, 0x0000001B, 0x00010001);
  4845. SCSenddata(pCI, WM_KEYUP, 0x0000001B, 0xC0010001);
  4846. SCSenddata(pCI, WM_KEYUP, 0x00000011, 0xC01D0001);
  4847. SCSendtextAsMsgs(pCI, pCI->pConfigInfo->strStartRun_Act);
  4848. }
  4849. else
  4850. {
  4851. TRACE((WARNING_MESSAGE,
  4852. "Keyboard hook disabled! Cannot send Win+R!\n"));
  4853. }
  4854. }
  4855. }
  4856. /*++
  4857. * Function:
  4858. * SCClientHandle
  4859. * Description:
  4860. * Get client window
  4861. * Arguments:
  4862. * pCI - connection context
  4863. * Return value:
  4864. * window handle found, NULL otherwise
  4865. * Called by:
  4866. *
  4867. --*/
  4868. PROTOCOLAPI
  4869. HWND
  4870. SMCAPI
  4871. SCGetClientWindowHandle(
  4872. PCONNECTINFO pCI
  4873. )
  4874. {
  4875. HWND hWnd;
  4876. hWnd = NULL;
  4877. if ( NULL == pCI )
  4878. {
  4879. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  4880. goto exitpt;
  4881. }
  4882. hWnd = pCI->hClient;
  4883. if (!hWnd)
  4884. {
  4885. TRACE((ERROR_MESSAGE, "SCGetClientHandle failed\n"));
  4886. goto exitpt;
  4887. }
  4888. exitpt:
  4889. return hWnd;
  4890. }