Source code of Windows XP (NT5)
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.

3890 lines
98 KiB

  1. /*++
  2. * File name:
  3. * scfuncs.c
  4. * Contents:
  5. * Functions exported to smclient intepreter
  6. *
  7. * Copyright (C) 1998-1999 Microsoft Corp.
  8. --*/
  9. #include <windows.h>
  10. #include <stdio.h>
  11. #include <malloc.h>
  12. #include <process.h>
  13. #include <string.h>
  14. #include <stdlib.h>
  15. #include <ctype.h>
  16. #include <stdlib.h>
  17. #include <io.h>
  18. #include <fcntl.h>
  19. #include <sys/stat.h>
  20. #include <winsock.h>
  21. #include "tclient.h"
  22. #define PROTOCOLAPI
  23. #include "protocol.h"
  24. #include "gdata.h"
  25. #include "queues.h"
  26. #include "misc.h"
  27. #include "rclx.h"
  28. #include "..\..\bmplib\bmplib.h"
  29. // This structure is used by _FindTopWindow
  30. typedef struct _SEARCHWND {
  31. TCHAR *szClassName; // The class name of searched window,
  32. // NULL - ignore
  33. TCHAR *szCaption; // Window caption, NULL - ignore
  34. LONG_PTR lProcessId; // Process Id of the owner, 0 - ignore
  35. HWND hWnd; // Found window handle
  36. } SEARCHWND, *PSEARCHWND;
  37. /*
  38. * Internal exported functions
  39. */
  40. LPCSTR Wait4Str(PCONNECTINFO, LPCWSTR);
  41. LPCSTR Wait4StrTimeout(PCONNECTINFO, LPCWSTR);
  42. LPCSTR Wait4MultipleStr(PCONNECTINFO, LPCWSTR);
  43. LPCSTR Wait4MultipleStrTimeout(PCONNECTINFO, LPCWSTR);
  44. LPCSTR GetWait4MultipleStrResult(PCONNECTINFO, LPCWSTR);
  45. LPCSTR GetFeedbackString(PCONNECTINFO, LPSTR result, UINT max);
  46. LPCSTR Wait4Disconnect(PCONNECTINFO);
  47. LPCSTR Wait4Connect(PCONNECTINFO);
  48. LPCSTR RegisterChat(PCONNECTINFO pCI, LPCWSTR lpszParam);
  49. LPCSTR UnregisterChat(PCONNECTINFO pCI, LPCWSTR lpszParam);
  50. LPCSTR GetDisconnectReason(PCONNECTINFO pCI);
  51. PROTOCOLAPI
  52. LPCSTR SMCAPI SCSendtextAsMsgs(PCONNECTINFO, LPCWSTR);
  53. PROTOCOLAPI
  54. LPCSTR SMCAPI SCSwitchToProcess(PCONNECTINFO pCI, LPCWSTR lpszParam);
  55. PROTOCOLAPI
  56. LPCSTR SMCAPI SCSetClientTopmost(PCONNECTINFO pCI, LPCWSTR lpszParam);
  57. PROTOCOLAPI
  58. LPCSTR SMCAPI SCSendMouseClick(PCONNECTINFO pCI, UINT xPos, UINT yPos);
  59. PROTOCOLAPI
  60. LPCSTR SMCAPI SCGetClientScreen(PCONNECTINFO pCI, INT left, INT top, INT right, INT bottom, UINT *puiSize, PVOID *ppDIB);
  61. PROTOCOLAPI
  62. LPCSTR SMCAPI SCSaveClientScreen(PCONNECTINFO pCI, INT left, INT top, INT right, INT bottom, LPCSTR szFileName);
  63. /*
  64. * Intenal functions definition
  65. */
  66. LPCSTR _Wait4ConnectTimeout(PCONNECTINFO pCI, DWORD dwTimeout);
  67. LPCSTR _Wait4ClipboardTimeout(PCONNECTINFO pCI, DWORD dwTimeout);
  68. LPCSTR _SendRClxData(PCONNECTINFO pCI, PRCLXDATA pRClxData);
  69. LPCSTR _Wait4RClxDataTimeout(PCONNECTINFO pCI, DWORD dwTimeout);
  70. LPCSTR _Wait4Str(PCONNECTINFO, LPCWSTR, DWORD dwTimeout, WAITTYPE);
  71. LPCSTR _WaitSomething(PCONNECTINFO pCI, PWAIT4STRING pWait, DWORD dwTimeout);
  72. VOID _CloseConnectInfo(PCONNECTINFO);
  73. LPCSTR _Login(PCONNECTINFO, LPCWSTR, LPCWSTR, LPCWSTR);
  74. HWND _FindTopWindow(LPTSTR, LPTSTR, LONG_PTR);
  75. HWND _FindWindow(HWND, LPTSTR, LPTSTR);
  76. BOOL _IsExtendedScanCode(INT scancode);
  77. /*
  78. * Clipboard help functions (clputil.c)
  79. */
  80. VOID
  81. Clp_GetClipboardData(
  82. UINT format,
  83. HGLOBAL hClipData,
  84. INT *pnClipDataSize,
  85. HGLOBAL *phNewData);
  86. BOOL
  87. Clp_SetClipboardData(
  88. UINT formatID,
  89. HGLOBAL hClipData,
  90. INT nClipDataSize,
  91. BOOL *pbFreeHandle);
  92. UINT
  93. Clp_GetClipboardFormat(LPCSTR szFormatLookup);
  94. BOOL
  95. Clp_EmptyClipboard(VOID);
  96. BOOL
  97. Clp_CheckEmptyClipboard(VOID);
  98. UINT
  99. _GetKnownClipboardFormatIDByName(LPCSTR szFormatName);
  100. /*++
  101. * Function:
  102. * SCInit
  103. * Description:
  104. * Called by smclient after the library is loaded.
  105. * Passes trace routine
  106. * Arguments:
  107. * pInitData - contains a trace routine
  108. * Called by:
  109. * !smclient
  110. --*/
  111. PROTOCOLAPI
  112. VOID
  113. SMCAPI
  114. SCInit(SCINITDATA *pInitData)
  115. {
  116. g_pfnPrintMessage = pInitData->pfnPrintMessage;
  117. }
  118. /*++
  119. * Function:
  120. * SCConnectEx
  121. * Description:
  122. * Called by smclient when connect command is interpreted
  123. * Arguments:
  124. * lpszServerName - server to connect to
  125. * lpszUserName - login user name. Empty string means no login
  126. * lpszPassword - login password
  127. * lpszDomain - login domain, empty string means login to a domain
  128. * the same as lpszServerName
  129. * xRes, yRes - clients resolution, 0x0 - default
  130. * ConnectFlags -
  131. * - low speed (compression) option
  132. * - cache the bitmaps to the disc option
  133. * - connection context allocated in this function
  134. * Return value:
  135. * Error message. NULL on success
  136. * Called by:
  137. * SCConnect
  138. --*/
  139. PROTOCOLAPI
  140. LPCSTR
  141. SMCAPI
  142. SCConnectEx(
  143. LPCWSTR lpszServerName,
  144. LPCWSTR lpszUserName,
  145. LPCWSTR lpszPassword,
  146. LPCWSTR lpszDomain,
  147. LPCWSTR lpszShell,
  148. IN const int xRes,
  149. IN const int yRes,
  150. IN const int ConnectionFlags,
  151. PCONNECTINFO *ppCI)
  152. {
  153. HWND hDialog, hClient, hConnect;
  154. HWND hContainer, hInput, hOutput;
  155. STARTUPINFO si;
  156. PROCESS_INFORMATION procinfo;
  157. LPCSTR rv = NULL;
  158. int trys;
  159. CHAR szCommandLine[MAX_STRING_LENGTH];
  160. LPCSTR szDiscon;
  161. UINT xxRes, yyRes;
  162. // Correct the resolution
  163. if (xRes >= 1600 && yRes >= 1200) {xxRes = 1600; yyRes = 1200;}
  164. else if (xRes >= 1280 && yRes >= 1024) {xxRes = 1280; yyRes = 1024;}
  165. else if (xRes >= 1024 && yRes >= 768) {xxRes = 1024; yyRes = 768;}
  166. else if (xRes >= 800 && yRes >= 600) {xxRes = 800; yyRes = 600;}
  167. else {xxRes = 640; yyRes = 480;}
  168. *ppCI = NULL;
  169. for (trys = 60; trys && !g_hWindow; trys--)
  170. Sleep(1000);
  171. if (!g_hWindow)
  172. {
  173. TRACE((ERROR_MESSAGE, "Panic !!! Feedback window is not created\n"));
  174. rv = ERR_WAIT_FAIL_TIMEOUT;
  175. goto exitpt;
  176. }
  177. *ppCI = (PCONNECTINFO)malloc(sizeof(**ppCI));
  178. if (!*ppCI)
  179. {
  180. TRACE((ERROR_MESSAGE,
  181. "Couldn't allocate %d bytes memory\n",
  182. sizeof(**ppCI)));
  183. rv = ERR_ALLOCATING_MEMORY;
  184. goto exitpt;
  185. }
  186. memset(*ppCI, 0, sizeof(**ppCI));
  187. (*ppCI)->OwnerThreadId = GetCurrentThreadId();
  188. // Check in what mode the client will be executed
  189. // if the server name starts with '\'
  190. // then tclient.dll will wait until some remote client
  191. // is connected (aka RCLX mode)
  192. // otherwise start the client on the same machine
  193. // running tclient.dll (smclient)
  194. if (*lpszServerName != L'\\')
  195. {
  196. // This is local mode, start the RDP client process
  197. FillMemory(&si, sizeof(si), 0);
  198. si.cb = sizeof(si);
  199. si.wShowWindow = SW_SHOWMINIMIZED;
  200. _SetClientRegistry(lpszServerName,
  201. lpszShell,
  202. xxRes, yyRes,
  203. ConnectionFlags);
  204. _snprintf(szCommandLine, sizeof(szCommandLine),
  205. #ifdef _WIN64
  206. "%s /CLXDLL=CLXTSHAR.DLL /CLXCMDLINE=%s%I64d " REG_FORMAT,
  207. #else // !_WIN64
  208. "%s /CLXDLL=CLXTSHAR.DLL /CLXCMDLINE=%s%d " REG_FORMAT,
  209. #endif // _WIN64
  210. g_strClientImg, _HWNDOPT,
  211. g_hWindow, GetCurrentProcessId(), GetCurrentThreadId());
  212. (*ppCI)->dead = FALSE;
  213. _AddToClientQ(*ppCI);
  214. if (!CreateProcessA(NULL,
  215. szCommandLine,
  216. NULL, // Security attribute for process
  217. NULL, // Security attribute for thread
  218. FALSE, // Inheritance - no
  219. 0, // Creation flags
  220. NULL, // Environment
  221. NULL, // Current dir
  222. &si,
  223. &procinfo))
  224. {
  225. TRACE((ERROR_MESSAGE,
  226. "Error creating process (szCmdLine=%s), GetLastError=0x%x\n",
  227. szCommandLine, GetLastError()));
  228. CloseHandle(procinfo.hProcess);
  229. CloseHandle(procinfo.hThread);
  230. procinfo.hProcess = procinfo.hProcess = NULL;
  231. rv = ERR_CREATING_PROCESS;
  232. goto exiterr;
  233. }
  234. (*ppCI)->hProcess = procinfo.hProcess;
  235. (*ppCI)->hThread = procinfo.hThread;
  236. (*ppCI)->lProcessId = procinfo.dwProcessId;
  237. (*ppCI)->dwThreadId = procinfo.dwThreadId;
  238. rv = Wait4Connect(*ppCI);
  239. if (rv || (*ppCI)->dead)
  240. {
  241. (*ppCI)->dead = TRUE;
  242. TRACE((WARNING_MESSAGE, "Client can't connect\n"));
  243. rv = ERR_CONNECTING;
  244. goto exiterr;
  245. }
  246. hClient = (*ppCI)->hClient;
  247. if ( NULL == hClient)
  248. {
  249. trys = 120; // 2 minutes
  250. do {
  251. hClient = _FindTopWindow(NAME_MAINCLASS,
  252. NULL,
  253. procinfo.dwProcessId);
  254. if (!hClient)
  255. {
  256. Sleep(1000);
  257. trys --;
  258. }
  259. } while(!hClient && trys);
  260. if (!trys)
  261. {
  262. TRACE((WARNING_MESSAGE, "Can't connect"));
  263. rv = ERR_CONNECTING;
  264. goto exiterr;
  265. }
  266. }
  267. // Find the clients child windows
  268. trys = 240; // 2 min
  269. do {
  270. hContainer = _FindWindow(hClient, NULL, NAME_CONTAINERCLASS);
  271. hInput = _FindWindow(hContainer, NULL, NAME_INPUT);
  272. hOutput = _FindWindow(hContainer, NULL, NAME_OUTPUT);
  273. if (!hContainer || !hInput || !hOutput)
  274. {
  275. TRACE((INFO_MESSAGE, "Can't get child windows. Retry"));
  276. Sleep(500);
  277. trys--;
  278. }
  279. } while ((!hContainer || !hInput || !hOutput) && trys);
  280. if (!trys)
  281. {
  282. TRACE((WARNING_MESSAGE, "Can't find child windows"));
  283. rv = ERR_CONNECTING;
  284. goto exiterr;
  285. }
  286. TRACE((INFO_MESSAGE, "hClient = 0x%x\n", hClient));
  287. TRACE((INFO_MESSAGE, "hContainer= 0x%x\n", hContainer));
  288. TRACE((INFO_MESSAGE, "hInput = 0x%x\n", hInput));
  289. TRACE((INFO_MESSAGE, "hOutput = 0x%x\n", hOutput));
  290. (*ppCI)->hClient = hClient;
  291. (*ppCI)->hContainer = hContainer;
  292. (*ppCI)->hInput = hInput;
  293. (*ppCI)->hOutput = hOutput;
  294. } else {
  295. // Else what !? This is RCLX mode
  296. // Go in wait mode and wait until some client is connected
  297. // remotely
  298. // set flag in context that this connection works only with remote client
  299. // find the valid server name
  300. while (*lpszServerName && (*lpszServerName) == L'\\')
  301. lpszServerName ++;
  302. TRACE((INFO_MESSAGE,
  303. "A thread in RCLX mode. Wait for some client."
  304. "The target is: %S\n", lpszServerName));
  305. (*ppCI)->dead = FALSE;
  306. (*ppCI)->RClxMode = TRUE;
  307. (*ppCI)->dwThreadId = GetCurrentThreadId();
  308. _AddToClientQ(*ppCI);
  309. rv = _Wait4ConnectTimeout(*ppCI, INFINITE);
  310. if (rv || (*ppCI)->dead)
  311. {
  312. (*ppCI)->dead = TRUE;
  313. TRACE((WARNING_MESSAGE, "Client can't connect to the test controler (us)\n"));
  314. rv = ERR_CONNECTING;
  315. goto exiterr;
  316. } else {
  317. // dwProcessId contains socket. hClient is pointer to RCLX
  318. // context structure, aren't they ?
  319. ASSERT((*ppCI)->lProcessId != INVALID_SOCKET);
  320. ASSERT((*ppCI)->hClient);
  321. TRACE((INFO_MESSAGE, "Client received remote connection\n"));
  322. }
  323. // Next, send connection info to the remote client
  324. // like server to connect to, resolution, etc.
  325. if (!RClx_SendConnectInfo(
  326. (PRCLXCONTEXT)((*ppCI)->hClient),
  327. lpszServerName,
  328. xxRes,
  329. yyRes,
  330. ConnectionFlags))
  331. {
  332. (*ppCI)->dead = TRUE;
  333. TRACE((WARNING_MESSAGE, "Client can't send connection info\n"));
  334. rv = ERR_CONNECTING;
  335. goto exiterr;
  336. }
  337. // Now again wait for connect event
  338. // this time it will be real
  339. rv = Wait4Connect(*ppCI);
  340. if ((*ppCI)->bWillCallAgain)
  341. {
  342. // if so, now the client is disconnected
  343. TRACE((INFO_MESSAGE, "Wait for second call\n"));
  344. (*ppCI)->dead = FALSE;
  345. rv = Wait4Connect(*ppCI);
  346. // Wait for second connect
  347. rv = Wait4Connect(*ppCI);
  348. }
  349. if (rv || (*ppCI)->dead)
  350. {
  351. (*ppCI)->dead = TRUE;
  352. TRACE((WARNING_MESSAGE, "Client(mstsc) can't connect to TS\n"));
  353. rv = ERR_CONNECTING;
  354. goto exiterr;
  355. }
  356. }
  357. // Save the resolution
  358. (*ppCI)->xRes = xxRes;
  359. (*ppCI)->yRes = yyRes;
  360. // If username is present try to login
  361. if (wcslen(lpszUserName))
  362. {
  363. rv = _Login(*ppCI, lpszUserName, lpszPassword, lpszDomain);
  364. if (rv)
  365. goto exiterr;
  366. }
  367. exitpt:
  368. return rv;
  369. exiterr:
  370. if (*ppCI)
  371. {
  372. if ((szDiscon = SCDisconnect(*ppCI)))
  373. {
  374. TRACE(( WARNING_MESSAGE, "Error disconnecting: %s\n", szDiscon));
  375. }
  376. *ppCI = NULL;
  377. }
  378. return rv;
  379. }
  380. PROTOCOLAPI
  381. LPCSTR
  382. SMCAPI
  383. SCConnect(
  384. LPCWSTR lpszServerName,
  385. LPCWSTR lpszUserName,
  386. LPCWSTR lpszPassword,
  387. LPCWSTR lpszDomain,
  388. IN const int xRes,
  389. IN const int yRes,
  390. PCONNECTINFO *ppCI)
  391. {
  392. return SCConnectEx(
  393. lpszServerName,
  394. lpszUserName,
  395. lpszPassword,
  396. lpszDomain,
  397. NULL, // Default shell (MS Explorer)
  398. xRes,
  399. yRes,
  400. g_ConnectionFlags, // compression, bmp cache, full screen
  401. ppCI);
  402. }
  403. /*++
  404. * Function:
  405. * SCDisconnect
  406. * Description:
  407. * Called by smclient, when disconnect command is interpreted
  408. * Arguments:
  409. * pCI - connection context
  410. * Return value:
  411. * Error message. NULL on success
  412. * Called by:
  413. * !smclient
  414. --*/
  415. PROTOCOLAPI
  416. LPCSTR
  417. SMCAPI
  418. SCDisconnect(
  419. PCONNECTINFO pCI)
  420. {
  421. LPCSTR rv = NULL;
  422. INT nCloseTime = WAIT4STR_TIMEOUT;
  423. INT nCloseTries = 0;
  424. DWORD wres;
  425. HWND hYesNo = NULL;
  426. HWND hDiscBox = NULL;
  427. HWND hDialog = NULL;
  428. if (!pCI)
  429. {
  430. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  431. rv = ERR_NULL_CONNECTINFO;
  432. goto exitpt;
  433. }
  434. if (!(pCI->RClxMode))
  435. {
  436. // Try to close the client window
  437. if (!pCI->hClient)
  438. pCI->hClient = _FindTopWindow(NAME_MAINCLASS,
  439. NULL,
  440. pCI->lProcessId);
  441. if (pCI->hClient)
  442. SendMessage(pCI->hClient, WM_CLOSE, 0, 0);
  443. do {
  444. // search for disconnect dialog and close it
  445. if (!hDialog && !hDiscBox &&
  446. (hDiscBox =
  447. _FindTopWindow(NULL,
  448. g_strDisconnectDialogBox,
  449. pCI->lProcessId)))
  450. PostMessage(hDiscBox, WM_CLOSE, 0, 0);
  451. // If the client asks whether to close or not
  452. // Answer with 'Yes'
  453. if (!hYesNo)
  454. hYesNo = _FindTopWindow(NULL,
  455. g_strYesNoShutdown,
  456. pCI->lProcessId);
  457. if (hYesNo &&
  458. (nCloseTries % 10) == 1)
  459. PostMessage(hYesNo, WM_KEYDOWN, VK_RETURN, 0);
  460. else if ((nCloseTries % 10) == 5)
  461. {
  462. // On every 10 attempts retry to close the client
  463. if (!pCI->hClient)
  464. pCI->hClient = _FindTopWindow(NAME_MAINCLASS,
  465. NULL,
  466. pCI->lProcessId);
  467. if (pCI->hClient)
  468. PostMessage(pCI->hClient, WM_CLOSE, 0, 0);
  469. }
  470. nCloseTries++;
  471. nCloseTime -= 3000;
  472. } while (
  473. (wres = WaitForSingleObject(pCI->hProcess, 3000)) ==
  474. WAIT_TIMEOUT &&
  475. nCloseTime > 0
  476. );
  477. if (wres == WAIT_TIMEOUT)
  478. {
  479. TRACE((WARNING_MESSAGE,
  480. "Can't close process. WaitForSingleObject timeouts\n"));
  481. TRACE((WARNING_MESSAGE,
  482. "Process #%d will be killed\n",
  483. pCI->lProcessId ));
  484. if (!TerminateProcess(pCI->hProcess, 1))
  485. {
  486. TRACE((WARNING_MESSAGE,
  487. "Can't kill process #%p. GetLastError=%d\n",
  488. pCI->lProcessId, GetLastError()));
  489. }
  490. }
  491. }
  492. if (!_RemoveFromClientQ(pCI))
  493. {
  494. TRACE(( WARNING_MESSAGE,
  495. "Couldn't find CONNECTINFO in the queue\n" ));
  496. }
  497. _CloseConnectInfo(pCI);
  498. exitpt:
  499. return rv;
  500. }
  501. /*++
  502. * Function:
  503. * SCLogoff
  504. * Description:
  505. * Called by smclient, when logoff command is interpreted
  506. * Arguments:
  507. * pCI - connection context
  508. * Return value:
  509. * Error message. NULL on success
  510. * Called by:
  511. * !smclient
  512. --*/
  513. PROTOCOLAPI
  514. LPCSTR
  515. SMCAPI
  516. SCLogoff(
  517. PCONNECTINFO pCI)
  518. {
  519. LPCSTR rv = NULL;
  520. INT retries = 5;
  521. if (!pCI)
  522. {
  523. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  524. rv = ERR_NULL_CONNECTINFO;
  525. goto exitpt;
  526. }
  527. if (pCI->dead)
  528. {
  529. rv = ERR_CLIENT_IS_DEAD;
  530. goto disconnectpt;
  531. }
  532. do {
  533. // Send Ctrl+Esc
  534. SCSenddata(pCI, WM_KEYDOWN, 17, 1900545);
  535. SCSenddata(pCI, WM_KEYDOWN, 27, 65537);
  536. SCSenddata(pCI, WM_KEYUP, 27, -1073676287);
  537. SCSenddata(pCI, WM_KEYUP, 17, -1071841279);
  538. // Wait for Run... menu
  539. rv = _Wait4Str(pCI, g_strStartRun, WAIT4STR_TIMEOUT/4, WAIT_STRING);
  540. if (rv)
  541. goto next_retry;
  542. // Send three times Key-Up (scan code 72) and <Enter>
  543. SCSendtextAsMsgs(pCI, g_strStartLogoff);
  544. rv = _Wait4Str(pCI,
  545. g_strNTSecurity,
  546. WAIT4STR_TIMEOUT/4,
  547. WAIT_STRING);
  548. next_retry:
  549. retries --;
  550. } while (rv && retries);
  551. if (rv)
  552. goto disconnectpt;
  553. for (retries = 5; retries; retries--) {
  554. SCSendtextAsMsgs(pCI, g_strNTSecurity_Act);
  555. rv = Wait4Str(pCI, g_strSureLogoff);
  556. if (!rv) break;
  557. }
  558. if (rv)
  559. goto disconnectpt;
  560. SCSendtextAsMsgs(pCI, g_strSureLogoffAct); // Press enter
  561. rv = Wait4Disconnect(pCI);
  562. if (rv)
  563. {
  564. TRACE((WARNING_MESSAGE, "Can't close the connection\n"));
  565. }
  566. disconnectpt:
  567. rv = SCDisconnect(pCI);
  568. exitpt:
  569. return rv;
  570. }
  571. /*++
  572. * Function:
  573. * SCStart
  574. * Description:
  575. * Called by smclient, when start command is interpreted
  576. * This functions emulates starting an app from Start->Run menu
  577. * on the server side
  578. * Arguments:
  579. * pCI - connection context
  580. * lpszAppName - command line
  581. * Return value:
  582. * Error message. NULL on success
  583. * Called by:
  584. * !smclient
  585. --*/
  586. PROTOCOLAPI
  587. LPCSTR
  588. SMCAPI
  589. SCStart(
  590. PCONNECTINFO pCI, LPCWSTR lpszAppName)
  591. {
  592. LPCSTR waitres = NULL;
  593. // int retries = 5;
  594. // int retries2 = 5;
  595. DWORD dwTimeout;
  596. LPCSTR rv = NULL;
  597. if (!pCI)
  598. {
  599. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  600. rv = ERR_NULL_CONNECTINFO;
  601. goto exitpt;
  602. }
  603. if (pCI->dead)
  604. {
  605. rv = ERR_CLIENT_IS_DEAD;
  606. goto exitpt;
  607. }
  608. dwTimeout = 10000; // start the timeout of 10 secs
  609. // Try to start run menu
  610. do {
  611. // Press Ctrl+Esc
  612. do {
  613. TRACE((ALIVE_MESSAGE, "Start: Sending Ctrl+Esc\n"));
  614. SCSenddata(pCI, WM_KEYDOWN, 17, 1900545);
  615. SCSenddata(pCI, WM_KEYDOWN, 27, 65537);
  616. SCSenddata(pCI, WM_KEYUP, 27, -1073676287);
  617. SCSenddata(pCI, WM_KEYUP, 17, -1071841279);
  618. // If the last wait was unsuccessfull increase the timeout
  619. if (waitres)
  620. dwTimeout += 2000;
  621. // Wait for Run... menu
  622. waitres = _Wait4Str(pCI,
  623. g_strStartRun,
  624. dwTimeout,
  625. WAIT_STRING);
  626. if (waitres)
  627. {
  628. TRACE((INFO_MESSAGE, "Start: Start menu didn't appear. Retrying\n"));
  629. } else {
  630. TRACE((ALIVE_MESSAGE, "Start: Got the start menu\n"));
  631. }
  632. } while (waitres && dwTimeout < WAIT4STR_TIMEOUT);
  633. if (waitres)
  634. {
  635. TRACE((WARNING_MESSAGE, "Start: Start menu didn't appear. Giving up\n"));
  636. rv = ERR_START_MENU_NOT_APPEARED;
  637. goto exitpt;
  638. }
  639. TRACE((ALIVE_MESSAGE, "Start: Waiting for the \"Run\" box\n"));
  640. // press 'R' for Run...
  641. SCSendtextAsMsgs(pCI, g_strStartRun_Act);
  642. waitres = _Wait4Str(pCI,
  643. g_strRunBox,
  644. dwTimeout,
  645. WAIT_STRING);
  646. if (waitres)
  647. // No success, press Esc
  648. {
  649. TRACE((INFO_MESSAGE, "Start: Can't get the \"Run\" box. Retrying\n"));
  650. SCSenddata(pCI, WM_KEYDOWN, 27, 65537);
  651. SCSenddata(pCI, WM_KEYUP, 27, -1073676287);
  652. }
  653. } while (waitres && dwTimeout < WAIT4STR_TIMEOUT);
  654. if (waitres)
  655. {
  656. TRACE((WARNING_MESSAGE, "Start: \"Run\" box didn't appear. Giving up\n"));
  657. rv = ERR_COULDNT_OPEN_PROGRAM;
  658. goto exitpt;
  659. }
  660. TRACE((ALIVE_MESSAGE, "Start: Sending the command line\n"));
  661. // Now we have the focus on the "Run" box, send the app name
  662. rv = SCSendtextAsMsgs(pCI, lpszAppName);
  663. // Hit <Enter>
  664. SCSenddata(pCI, WM_KEYDOWN, 13, 1835009);
  665. SCSenddata(pCI, WM_KEYUP, 13, -1071906815);
  666. exitpt:
  667. return rv;
  668. }
  669. // Eventualy, we are going to change the clipboard
  670. // Syncronize this, so no other thread's AV while
  671. // checking the clipboard content
  672. // store 1 for write, 0 for read
  673. static LONG g_ClipOpened = 0;
  674. /*++
  675. * Function:
  676. * SCClipbaord
  677. * Description:
  678. * Called by smclient, when clipboard command is interpreted
  679. * when eClipOp is COPY_TO_CLIPBOARD it copies the lpszFileName to
  680. * the clipboard. If eClipOp is PASTE_FROM_CLIPBOARD it
  681. * checks the clipboard content against the content of lpszFileName
  682. * Arguments:
  683. * pCI - connection context
  684. * eClipOp - clipboard operation. Possible values:
  685. * COPY_TO_CLIPBOARD and PASTE_FROM_CLIPBOARD
  686. * Return value:
  687. * Error message. NULL on success
  688. * Called by:
  689. * !smclient
  690. --*/
  691. PROTOCOLAPI
  692. LPCSTR
  693. SMCAPI
  694. SCClipboard(
  695. PCONNECTINFO pCI, const CLIPBOARDOPS eClipOp, LPCSTR lpszFileName)
  696. {
  697. LPCSTR rv = NULL;
  698. INT hFile = -1;
  699. LONG clplength = 0;
  700. UINT uiFormat = 0;
  701. HGLOBAL ghClipData = NULL;
  702. HGLOBAL hNewData = NULL;
  703. PBYTE pClipData = NULL;
  704. BOOL bClipboardOpen = FALSE;
  705. BOOL bFreeClipHandle = TRUE;
  706. LONG prevOp = 1;
  707. if (!pCI)
  708. {
  709. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  710. rv = ERR_NULL_CONNECTINFO;
  711. goto exitpt;
  712. }
  713. if (pCI->dead)
  714. {
  715. rv = ERR_CLIENT_IS_DEAD;
  716. goto exitpt;
  717. }
  718. if (lpszFileName == NULL || !(*lpszFileName))
  719. {
  720. // No filename specified, work like an empty clipboard is requested
  721. if (eClipOp == COPY_TO_CLIPBOARD)
  722. if (pCI->RClxMode)
  723. {
  724. if (!RClx_SendClipboard((PRCLXCONTEXT)(pCI->hClient),
  725. NULL, 0, 0))
  726. rv = ERR_COPY_CLIPBOARD;
  727. } else {
  728. if (!Clp_EmptyClipboard())
  729. rv = ERR_COPY_CLIPBOARD;
  730. }
  731. else if (eClipOp == PASTE_FROM_CLIPBOARD)
  732. {
  733. if (pCI->RClxMode)
  734. {
  735. if (!RClx_SendClipboardRequest((PRCLXCONTEXT)(pCI->hClient), 0))
  736. {
  737. rv = ERR_PASTE_CLIPBOARD;
  738. goto exitpt;
  739. }
  740. if (_Wait4ClipboardTimeout(pCI, WAIT4STR_TIMEOUT))
  741. {
  742. rv = ERR_PASTE_CLIPBOARD;
  743. goto exitpt;
  744. }
  745. // We do not expect to receive clipboard data
  746. // just format ID
  747. if (!pCI->uiClipboardFormat)
  748. // if the format is 0, then there's no clipboard
  749. rv = NULL;
  750. else
  751. rv = ERR_PASTE_CLIPBOARD_DIFFERENT_SIZE;
  752. } else {
  753. if (Clp_CheckEmptyClipboard())
  754. rv = NULL;
  755. else
  756. rv = ERR_PASTE_CLIPBOARD_DIFFERENT_SIZE;
  757. }
  758. } else {
  759. TRACE((ERROR_MESSAGE, "SCClipboard: Invalid filename\n"));
  760. rv = ERR_INVALID_PARAM;
  761. }
  762. goto exitpt;
  763. }
  764. if (eClipOp == COPY_TO_CLIPBOARD)
  765. {
  766. // Open the file for reading
  767. hFile = _open(lpszFileName, _O_RDONLY|_O_BINARY);
  768. if (hFile == -1)
  769. {
  770. TRACE((ERROR_MESSAGE,
  771. "Error opening file: %s. errno=%d\n", lpszFileName, errno));
  772. rv = ERR_COPY_CLIPBOARD;
  773. goto exitpt;
  774. }
  775. // Get the clipboard length (in the file)
  776. clplength = _filelength(hFile) - sizeof(uiFormat);
  777. // Get the format
  778. if (_read(hFile, &uiFormat, sizeof(uiFormat)) != sizeof(uiFormat))
  779. {
  780. TRACE((ERROR_MESSAGE,
  781. "Error reading from file. errno=%d\n", errno));
  782. rv = ERR_COPY_CLIPBOARD;
  783. goto exitpt;
  784. }
  785. ghClipData = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, clplength);
  786. if (!ghClipData)
  787. {
  788. TRACE((ERROR_MESSAGE,
  789. "Can't allocate %d bytes\n", clplength));
  790. rv = ERR_COPY_CLIPBOARD;
  791. goto exitpt;
  792. }
  793. pClipData = GlobalLock(ghClipData);
  794. if (!pClipData)
  795. {
  796. TRACE((ERROR_MESSAGE,
  797. "Can't lock handle 0x%x\n", ghClipData));
  798. rv = ERR_COPY_CLIPBOARD;
  799. goto exitpt;
  800. }
  801. if (_read(hFile, pClipData, clplength) != clplength)
  802. {
  803. TRACE((ERROR_MESSAGE,
  804. "Error reading from file. errno=%d\n", errno));
  805. rv = ERR_COPY_CLIPBOARD;
  806. goto exitpt;
  807. }
  808. GlobalUnlock(ghClipData);
  809. if (pCI->RClxMode)
  810. // RCLX mode, send the data to the client's machine
  811. {
  812. if (!(pClipData = GlobalLock(ghClipData)))
  813. {
  814. rv = ERR_COPY_CLIPBOARD;
  815. goto exitpt;
  816. }
  817. if (!RClx_SendClipboard((PRCLXCONTEXT)(pCI->hClient),
  818. pClipData, clplength, uiFormat))
  819. {
  820. rv = ERR_COPY_CLIPBOARD;
  821. goto exitpt;
  822. }
  823. } else {
  824. // Local mode, change the clipboard on this machine
  825. if ((prevOp = InterlockedExchange(&g_ClipOpened, 1)))
  826. {
  827. rv = ERR_CLIPBOARD_LOCKED;
  828. goto exitpt;
  829. }
  830. if (!OpenClipboard(NULL))
  831. {
  832. TRACE((ERROR_MESSAGE,
  833. "Can't open the clipboard. GetLastError=%d\n",
  834. GetLastError()));
  835. rv = ERR_COPY_CLIPBOARD;
  836. goto exitpt;
  837. }
  838. bClipboardOpen = TRUE;
  839. // Empty the clipboard, so we'll have only one entry
  840. EmptyClipboard();
  841. if (!Clp_SetClipboardData(uiFormat, ghClipData, clplength, &bFreeClipHandle))
  842. {
  843. TRACE((ERROR_MESSAGE,
  844. "SetClipboardData failed. GetLastError=%d\n",
  845. GetLastError()));
  846. rv = ERR_COPY_CLIPBOARD;
  847. goto exitpt;
  848. }
  849. }
  850. } else if (eClipOp == PASTE_FROM_CLIPBOARD)
  851. {
  852. LONG nClipDataSize;
  853. // Open the file for reading
  854. hFile = _open(lpszFileName, _O_RDONLY|_O_BINARY);
  855. if (hFile == -1)
  856. {
  857. TRACE((ERROR_MESSAGE,
  858. "Error opening file: %s. errno=%d\n", lpszFileName, errno));
  859. rv = ERR_PASTE_CLIPBOARD;
  860. goto exitpt;
  861. }
  862. // Get the clipboard length (in the file)
  863. clplength = _filelength(hFile) - sizeof(uiFormat);
  864. // Get the format
  865. if (_read(hFile, &uiFormat, sizeof(uiFormat)) != sizeof(uiFormat))
  866. {
  867. TRACE((ERROR_MESSAGE,
  868. "Error reading from file. errno=%d\n", errno));
  869. rv = ERR_PASTE_CLIPBOARD;
  870. goto exitpt;
  871. }
  872. // This piece retrieves the clipboard
  873. if (pCI->RClxMode)
  874. // Send request for a clipboard
  875. {
  876. if (!RClx_SendClipboardRequest((PRCLXCONTEXT)(pCI->hClient), uiFormat))
  877. {
  878. rv = ERR_PASTE_CLIPBOARD;
  879. goto exitpt;
  880. }
  881. if (_Wait4ClipboardTimeout(pCI, WAIT4STR_TIMEOUT))
  882. {
  883. rv = ERR_PASTE_CLIPBOARD;
  884. goto exitpt;
  885. }
  886. ghClipData = pCI->ghClipboard;
  887. // Get the clipboard size
  888. nClipDataSize = pCI->nClipboardSize;
  889. } else {
  890. // retrieve the local clipboard
  891. if ((prevOp = InterlockedExchange(&g_ClipOpened, 1)))
  892. {
  893. rv = ERR_CLIPBOARD_LOCKED;
  894. goto exitpt;
  895. }
  896. if (!OpenClipboard(NULL))
  897. {
  898. TRACE((ERROR_MESSAGE,
  899. "Can't open the clipboard. GetLastError=%d\n",
  900. GetLastError()));
  901. rv = ERR_PASTE_CLIPBOARD;
  902. goto exitpt;
  903. }
  904. bClipboardOpen = TRUE;
  905. // Retrieve the data
  906. ghClipData = GetClipboardData(uiFormat);
  907. if (ghClipData)
  908. {
  909. Clp_GetClipboardData(uiFormat,
  910. ghClipData,
  911. &nClipDataSize,
  912. &hNewData);
  913. bFreeClipHandle = FALSE;
  914. }
  915. if (hNewData)
  916. ghClipData = hNewData;
  917. }
  918. if (!ghClipData)
  919. {
  920. TRACE((ERROR_MESSAGE,
  921. "Can't get clipboard data (empty clipboard ?). GetLastError=%d\n",
  922. GetLastError()));
  923. rv = ERR_PASTE_CLIPBOARD_EMPTY;
  924. goto exitpt;
  925. }
  926. if (!nClipDataSize)
  927. TRACE((WARNING_MESSAGE, "Clipboard is empty.\n"));
  928. pClipData = GlobalLock(ghClipData);
  929. if (!pClipData)
  930. {
  931. TRACE((ERROR_MESSAGE,
  932. "Can't lock global mem. GetLastError=%d\n",
  933. GetLastError()));
  934. rv = ERR_PASTE_CLIPBOARD;
  935. goto exitpt;
  936. }
  937. // Check if the client is on Win16 platform
  938. // and the clipboard is paragraph aligned
  939. // the file size is just bellow this size
  940. if (pCI->RClxMode &&
  941. (strstr(pCI->szClientType, "WIN16") != NULL) &&
  942. ((nClipDataSize % 16) == 0) &&
  943. ((nClipDataSize - clplength) < 16) &&
  944. (nClipDataSize != 0))
  945. {
  946. // if so, then cut the clipboard size with the difference
  947. nClipDataSize = clplength;
  948. }
  949. else if (nClipDataSize != clplength)
  950. {
  951. TRACE((INFO_MESSAGE, "Different length: file=%d, clipbrd=%d\n",
  952. clplength, nClipDataSize));
  953. rv = ERR_PASTE_CLIPBOARD_DIFFERENT_SIZE;
  954. goto exitpt;
  955. }
  956. // compare the data
  957. {
  958. BYTE pBuff[1024];
  959. PBYTE pClp = pClipData;
  960. UINT nBytes;
  961. BOOL bEqu = TRUE;
  962. while (bEqu &&
  963. (nBytes = _read(hFile, pBuff, sizeof(pBuff))) &&
  964. nBytes != -1)
  965. {
  966. if (memcmp(pBuff, pClp, nBytes))
  967. bEqu = FALSE;
  968. pClp += nBytes;
  969. }
  970. if (!bEqu)
  971. {
  972. TRACE((INFO_MESSAGE, "Clipboard and file are not equal\n"));
  973. rv = ERR_PASTE_CLIPBOARD_NOT_EQUAL;
  974. }
  975. }
  976. } else
  977. rv = ERR_UNKNOWN_CLIPBOARD_OP;
  978. exitpt:
  979. // Do the cleanup
  980. // Release the clipboard handle
  981. if (pClipData)
  982. GlobalUnlock(ghClipData);
  983. // free any clipboard received in RCLX mode
  984. if (pCI->RClxMode && pCI->ghClipboard)
  985. {
  986. GlobalFree(pCI->ghClipboard);
  987. pCI->ghClipboard = NULL;
  988. }
  989. else if (ghClipData && eClipOp == COPY_TO_CLIPBOARD && bFreeClipHandle)
  990. GlobalFree(ghClipData);
  991. if (hNewData)
  992. GlobalFree(hNewData);
  993. // Close the file
  994. if (hFile != -1)
  995. _close(hFile);
  996. // Close the clipboard
  997. if (bClipboardOpen)
  998. CloseClipboard();
  999. if (!prevOp)
  1000. InterlockedExchange(&g_ClipOpened, 0);
  1001. return rv;
  1002. }
  1003. /*++
  1004. * Function:
  1005. * SCSaveClipboard
  1006. * Description:
  1007. * Save the clipboard in file (szFileName) with
  1008. * format specified in szFormatName
  1009. * Arguments:
  1010. * pCI - connection context
  1011. * szFormatName- format name
  1012. * szFileName - the name of the file to save to
  1013. * Return value:
  1014. * Error message. NULL on success
  1015. * Called by:
  1016. * !perlext
  1017. --*/
  1018. PROTOCOLAPI
  1019. LPCSTR
  1020. SMCAPI
  1021. SCSaveClipboard(
  1022. PCONNECTINFO pCI,
  1023. LPCSTR szFormatName,
  1024. LPCSTR szFileName)
  1025. {
  1026. LPCSTR rv = ERR_SAVE_CLIPBOARD;
  1027. BOOL bClipboardOpen = FALSE;
  1028. UINT nFormatID = 0;
  1029. HGLOBAL ghClipData = NULL;
  1030. HGLOBAL hNewData = NULL;
  1031. INT nClipDataSize;
  1032. CHAR *pClipData = NULL;
  1033. INT hFile = -1;
  1034. LONG prevOp = 1;
  1035. // ++++++ First go thru parameter check
  1036. if (!pCI)
  1037. {
  1038. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1039. rv = ERR_NULL_CONNECTINFO;
  1040. goto exitpt;
  1041. }
  1042. if (pCI->dead)
  1043. {
  1044. rv = ERR_CLIENT_IS_DEAD;
  1045. goto exitpt;
  1046. }
  1047. if (szFormatName == NULL || !(*szFormatName))
  1048. {
  1049. TRACE((ERROR_MESSAGE, "SCClipboard: Invalid format name\n"));
  1050. rv = ERR_INVALID_PARAM;
  1051. goto exitpt;
  1052. }
  1053. if (szFileName == NULL || !(*szFileName))
  1054. {
  1055. TRACE((ERROR_MESSAGE, "SCClipboard: Invalid filename\n"));
  1056. rv = ERR_INVALID_PARAM;
  1057. goto exitpt;
  1058. }
  1059. // ------ End of parameter check
  1060. //
  1061. if (pCI->RClxMode)
  1062. {
  1063. nFormatID = _GetKnownClipboardFormatIDByName(szFormatName);
  1064. if (!nFormatID)
  1065. {
  1066. TRACE((ERROR_MESSAGE, "Can't get the clipboard format ID: %s.\n", szFormatName));
  1067. goto exitpt;
  1068. }
  1069. // Send request for a clipboard
  1070. if (!RClx_SendClipboardRequest((PRCLXCONTEXT)(pCI->hClient), nFormatID))
  1071. {
  1072. rv = ERR_PASTE_CLIPBOARD;
  1073. goto exitpt;
  1074. }
  1075. if (_Wait4ClipboardTimeout(pCI, WAIT4STR_TIMEOUT))
  1076. {
  1077. rv = ERR_PASTE_CLIPBOARD;
  1078. goto exitpt;
  1079. }
  1080. ghClipData = pCI->ghClipboard;
  1081. // Get the clipboard size
  1082. nClipDataSize = pCI->nClipboardSize;
  1083. if (!ghClipData || !nClipDataSize)
  1084. {
  1085. TRACE((WARNING_MESSAGE, "Clipboard is empty.\n"));
  1086. goto exitpt;
  1087. }
  1088. } else {
  1089. // local mode
  1090. // Open the clipboard
  1091. if ((prevOp = InterlockedExchange(&g_ClipOpened, 1)))
  1092. {
  1093. rv = ERR_CLIPBOARD_LOCKED;
  1094. goto exitpt;
  1095. }
  1096. if (!OpenClipboard(NULL))
  1097. {
  1098. TRACE((ERROR_MESSAGE, "Can't open the clipboard. GetLastError=%d\n",
  1099. GetLastError()));
  1100. goto exitpt;
  1101. }
  1102. bClipboardOpen = TRUE;
  1103. nFormatID = Clp_GetClipboardFormat(szFormatName);
  1104. if (!nFormatID)
  1105. {
  1106. TRACE((ERROR_MESSAGE, "Can't get the clipboard format: %s.\n", szFormatName));
  1107. goto exitpt;
  1108. }
  1109. TRACE((INFO_MESSAGE, "Format ID: %d(0x%X)\n", nFormatID, nFormatID));
  1110. // Retrieve the data
  1111. ghClipData = GetClipboardData(nFormatID);
  1112. if (!ghClipData)
  1113. {
  1114. TRACE((ERROR_MESSAGE, "Can't get clipboard data. GetLastError=%d\n", GetLastError()));
  1115. goto exitpt;
  1116. }
  1117. Clp_GetClipboardData(nFormatID, ghClipData, &nClipDataSize, &hNewData);
  1118. if (hNewData)
  1119. ghClipData = hNewData;
  1120. if (!nClipDataSize)
  1121. {
  1122. TRACE((WARNING_MESSAGE, "Clipboard is empty.\n"));
  1123. goto exitpt;
  1124. }
  1125. }
  1126. pClipData = GlobalLock(ghClipData);
  1127. if (!pClipData)
  1128. {
  1129. TRACE((ERROR_MESSAGE, "Can't lock global mem. GetLastError=%d\n", GetLastError()));
  1130. goto exitpt;
  1131. }
  1132. // Open the destination file
  1133. hFile = _open(szFileName,
  1134. _O_RDWR|_O_CREAT|_O_BINARY|_O_TRUNC,
  1135. _S_IREAD|_S_IWRITE);
  1136. if (hFile == -1)
  1137. {
  1138. TRACE((ERROR_MESSAGE, "Can't open a file: %s\n", szFileName));
  1139. goto exitpt;
  1140. }
  1141. // First write the format type
  1142. if (_write(hFile, &nFormatID, sizeof(nFormatID)) != sizeof(nFormatID))
  1143. {
  1144. TRACE((ERROR_MESSAGE, "_write failed. errno=%d\n", errno));
  1145. goto exitpt;
  1146. }
  1147. if (_write(hFile, pClipData, nClipDataSize) != (INT)nClipDataSize)
  1148. {
  1149. TRACE((ERROR_MESSAGE, "_write failed. errno=%d\n", errno));
  1150. goto exitpt;
  1151. }
  1152. TRACE((INFO_MESSAGE, "File written successfully. %d bytes written\n", nClipDataSize));
  1153. rv = NULL;
  1154. exitpt:
  1155. // Do the cleanup
  1156. // Close the file
  1157. if (hFile != -1)
  1158. _close(hFile);
  1159. // Release the clipboard handle
  1160. if (pClipData)
  1161. GlobalUnlock(ghClipData);
  1162. if (hNewData)
  1163. GlobalFree(hNewData);
  1164. // Close the clipboard
  1165. if (bClipboardOpen)
  1166. CloseClipboard();
  1167. if (!prevOp)
  1168. InterlockedExchange(&g_ClipOpened, 0);
  1169. // free any clipboard received in RCLX mode
  1170. if (pCI && pCI->RClxMode && pCI->ghClipboard)
  1171. {
  1172. GlobalFree(pCI->ghClipboard);
  1173. pCI->ghClipboard = NULL;
  1174. }
  1175. return rv;
  1176. }
  1177. /*++
  1178. * Function:
  1179. * SCSenddata
  1180. * Description:
  1181. * Called by smclient, when senddata command is interpreted
  1182. * Sends an window message to the client
  1183. * Arguments:
  1184. * pCI - connection context
  1185. * uiMessage - the massage Id
  1186. * wParam - word param of the message
  1187. * lParam - long param of the message
  1188. * Return value:
  1189. * Error message. NULL on success
  1190. * Called by:
  1191. * !smclient
  1192. --*/
  1193. PROTOCOLAPI
  1194. LPCSTR
  1195. SMCAPI
  1196. SCSenddata(
  1197. PCONNECTINFO pCI,
  1198. const UINT uiMessage,
  1199. const WPARAM wParam,
  1200. const LPARAM lParam)
  1201. {
  1202. UINT msg = uiMessage;
  1203. LPCSTR rv = NULL;
  1204. if (!pCI)
  1205. {
  1206. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1207. rv = ERR_NULL_CONNECTINFO;
  1208. goto exitpt;
  1209. }
  1210. if (pCI->dead)
  1211. {
  1212. rv = ERR_CLIENT_IS_DEAD;
  1213. goto exitpt;
  1214. }
  1215. // TRACE((ALIVE_MESSAGE, "Senddata: uMsg=%x wParam=%x lParam=%x\n",
  1216. // uiMessage, wParam, lParam));
  1217. // Determines whether it will
  1218. // send the message to local window
  1219. // or thru RCLX
  1220. if (!pCI->RClxMode)
  1221. {
  1222. // Obsolete, a client registry setting "Allow Background Input" asserts
  1223. // that the client will accept the message
  1224. // SetFocus(pCI->hInput);
  1225. // SendMessage(pCI->hInput, WM_SETFOCUS, 0, 0);
  1226. SendMessage(pCI->hInput, msg, wParam, lParam);
  1227. } else {
  1228. // RClxMode
  1229. ASSERT(pCI->lProcessId != INVALID_SOCKET);
  1230. ASSERT(pCI->hClient);
  1231. if (!RClx_SendMessage((PRCLXCONTEXT)(pCI->hClient),
  1232. msg, wParam, lParam))
  1233. {
  1234. TRACE((WARNING_MESSAGE,
  1235. "Can't send message thru RCLX\n"));
  1236. }
  1237. }
  1238. exitpt:
  1239. return rv;
  1240. }
  1241. PROTOCOLAPI
  1242. LPCSTR
  1243. SMCAPI
  1244. SCClientTerminate(PCONNECTINFO pCI)
  1245. {
  1246. LPCSTR rv = ERR_CLIENTTERMINATE_FAIL;
  1247. if (!pCI)
  1248. {
  1249. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1250. rv = ERR_NULL_CONNECTINFO;
  1251. goto exitpt;
  1252. }
  1253. if (!(pCI->RClxMode))
  1254. {
  1255. if (!TerminateProcess(pCI->hProcess, 1))
  1256. {
  1257. TRACE((WARNING_MESSAGE,
  1258. "Can't kill process #%p. GetLastError=%d\n",
  1259. pCI->lProcessId, GetLastError()));
  1260. goto exitpt;
  1261. }
  1262. } else {
  1263. TRACE((WARNING_MESSAGE,
  1264. "ClientTerminate is not supported in RCLX mode yet\n"));
  1265. TRACE((WARNING_MESSAGE, "Using disconnect\n"));
  1266. }
  1267. rv = SCDisconnect(pCI);
  1268. exitpt:
  1269. return rv;
  1270. }
  1271. /*++
  1272. * Function:
  1273. * SCGetSessionId
  1274. * Description:
  1275. * Called by smclient, returns the session ID. 0 is invalid, not logged on
  1276. * yet
  1277. * Arguments:
  1278. * pCI - connection context
  1279. * Return value:
  1280. * session id, 0 is invlid value, -1 is returned on NT4 clients
  1281. * Called by:
  1282. * !smclient
  1283. --*/
  1284. PROTOCOLAPI
  1285. UINT
  1286. SMCAPI
  1287. SCGetSessionId(PCONNECTINFO pCI)
  1288. {
  1289. UINT rv = 0;
  1290. if (!pCI)
  1291. {
  1292. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1293. goto exitpt;
  1294. }
  1295. if (pCI->dead)
  1296. {
  1297. goto exitpt;
  1298. }
  1299. rv = pCI->uiSessionId;
  1300. exitpt:
  1301. return rv;
  1302. }
  1303. /*++
  1304. * Function:
  1305. * SCCheck
  1306. * Description:
  1307. * Called by smclient, when check command is interpreted
  1308. * Arguments:
  1309. * pCI - connection context
  1310. * lpszCommand - command name
  1311. * lpszParam - command parameter
  1312. * Return value:
  1313. * Error message. NULL on success. Exceptions are GetDisconnectReason and
  1314. * GetWait4MultipleStrResult
  1315. * Called by:
  1316. * !smclient
  1317. --*/
  1318. PROTOCOLAPI
  1319. LPCSTR
  1320. SMCAPI
  1321. SCCheck(PCONNECTINFO pCI, LPCSTR lpszCommand, LPCWSTR lpszParam)
  1322. {
  1323. LPCSTR rv = ERR_INVALID_COMMAND;
  1324. if (!pCI)
  1325. {
  1326. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1327. rv = ERR_NULL_CONNECTINFO;
  1328. goto exitpt;
  1329. }
  1330. if (pCI->dead)
  1331. {
  1332. rv = ERR_CLIENT_IS_DEAD;
  1333. goto exitpt;
  1334. }
  1335. if ( !_stricmp(lpszCommand, "Wait4Str"))
  1336. rv = Wait4Str(pCI, lpszParam);
  1337. else if (!_stricmp(lpszCommand, "Wait4Disconnect"))
  1338. rv = Wait4Disconnect(pCI);
  1339. else if (!_stricmp(lpszCommand, "RegisterChat"))
  1340. rv = RegisterChat(pCI, lpszParam);
  1341. else if (!_stricmp(lpszCommand, "UnregisterChat"))
  1342. rv = UnregisterChat(pCI, lpszParam);
  1343. else if (!_stricmp(lpszCommand, "GetDisconnectReason"))
  1344. rv = GetDisconnectReason(pCI);
  1345. else if (!_stricmp(lpszCommand, "Wait4StrTimeout"))
  1346. rv = Wait4StrTimeout(pCI, lpszParam);
  1347. else if (!_stricmp(lpszCommand, "Wait4MultipleStr"))
  1348. rv = Wait4MultipleStr(pCI, lpszParam);
  1349. else if (!_stricmp(lpszCommand, "Wait4MultipleStrTimeout"))
  1350. rv = Wait4MultipleStrTimeout(pCI, lpszParam);
  1351. else if (!_stricmp(lpszCommand, "GetWait4MultipleStrResult"))
  1352. rv = GetWait4MultipleStrResult(pCI, lpszParam);
  1353. else if (!_stricmp(lpszCommand, "SwitchToProcess"))
  1354. rv = SCSwitchToProcess(pCI, lpszParam);
  1355. /* **New** */
  1356. else if (!_stricmp(lpszCommand, "SetClientTopmost"))
  1357. rv = SCSetClientTopmost(pCI, lpszParam);
  1358. exitpt:
  1359. return rv;
  1360. }
  1361. /*
  1362. * Extensions and help functions
  1363. */
  1364. /*++
  1365. * Function:
  1366. * Wait4Disconnect
  1367. * Description:
  1368. * Waits until the client is disconnected
  1369. * Arguments:
  1370. * pCI - connection context
  1371. * Return value:
  1372. * Error message. NULL on success
  1373. * Called by:
  1374. * SCCheck, SCLogoff
  1375. --*/
  1376. LPCSTR Wait4Disconnect(PCONNECTINFO pCI)
  1377. {
  1378. WAIT4STRING Wait;
  1379. LPCSTR rv = NULL;
  1380. if (!pCI)
  1381. {
  1382. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1383. rv = ERR_NULL_CONNECTINFO;
  1384. goto exitpt;
  1385. }
  1386. if (pCI->dead)
  1387. {
  1388. rv = ERR_CLIENT_IS_DEAD;
  1389. goto exitpt;
  1390. }
  1391. memset(&Wait, 0, sizeof(Wait));
  1392. Wait.evWait = CreateEvent(NULL, //security
  1393. TRUE, //manual
  1394. FALSE, //initial state
  1395. NULL); //name
  1396. Wait.lProcessId = pCI->lProcessId;
  1397. Wait.pOwner = pCI;
  1398. Wait.WaitType = WAIT_DISC;
  1399. rv = _WaitSomething(pCI, &Wait, WAIT4STR_TIMEOUT);
  1400. if (!rv)
  1401. {
  1402. TRACE(( INFO_MESSAGE, "Client is disconnected\n"));
  1403. }
  1404. CloseHandle(Wait.evWait);
  1405. exitpt:
  1406. return rv;
  1407. }
  1408. /*++
  1409. * Function:
  1410. * Wait4Connect
  1411. * Description:
  1412. * Waits until the client is connect
  1413. * Arguments:
  1414. * pCI - connection context
  1415. * Return value:
  1416. * Error message, NULL on success
  1417. * Called by:
  1418. * SCCOnnect
  1419. --*/
  1420. LPCSTR Wait4Connect(PCONNECTINFO pCI)
  1421. {
  1422. return (_Wait4ConnectTimeout(pCI, CONNECT_TIMEOUT));
  1423. }
  1424. /*++
  1425. * Function:
  1426. * _Wait4ConnectTimeout
  1427. * Description:
  1428. * Waits until the client is connect
  1429. * Arguments:
  1430. * pCI - connection context
  1431. * dwTimeout - timeout value
  1432. * Return value:
  1433. * Error message, NULL on success
  1434. * Called by:
  1435. * SCConnect
  1436. --*/
  1437. LPCSTR _Wait4ConnectTimeout(PCONNECTINFO pCI, DWORD dwTimeout)
  1438. {
  1439. WAIT4STRING Wait;
  1440. LPCSTR rv = NULL;
  1441. if (!pCI)
  1442. {
  1443. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1444. rv = ERR_NULL_CONNECTINFO;
  1445. goto exitpt;
  1446. }
  1447. memset(&Wait, 0, sizeof(Wait));
  1448. Wait.evWait = CreateEvent(NULL, //security
  1449. TRUE, //manual
  1450. FALSE, //initial state
  1451. NULL); //name
  1452. Wait.lProcessId = pCI->lProcessId;
  1453. Wait.pOwner = pCI;
  1454. Wait.WaitType = WAIT_CONN;
  1455. rv = _WaitSomething(pCI, &Wait, dwTimeout);
  1456. if (!rv)
  1457. {
  1458. TRACE(( INFO_MESSAGE, "Client is connected\n"));
  1459. }
  1460. CloseHandle(Wait.evWait);
  1461. exitpt:
  1462. return rv;
  1463. }
  1464. /*++
  1465. * Function:
  1466. * _Wait4ClipboardTimeout
  1467. * Description:
  1468. * Waits until clipboard response is received from RCLX module
  1469. * Arguments:
  1470. * pCI - connection context
  1471. * dwTimeout - timeout value
  1472. * Return value:
  1473. * Error message, NULL on success
  1474. * Called by:
  1475. * SCClipboard
  1476. --*/
  1477. LPCSTR _Wait4ClipboardTimeout(PCONNECTINFO pCI, DWORD dwTimeout)
  1478. {
  1479. WAIT4STRING Wait;
  1480. LPCSTR rv = NULL;
  1481. if (!pCI)
  1482. {
  1483. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1484. rv = ERR_NULL_CONNECTINFO;
  1485. goto exitpt;
  1486. }
  1487. if (!(pCI->RClxMode))
  1488. {
  1489. TRACE((WARNING_MESSAGE, "WaitForClipboard: Not in RCLX mode\n"));
  1490. rv = ERR_NULL_CONNECTINFO;
  1491. goto exitpt;
  1492. }
  1493. memset(&Wait, 0, sizeof(Wait));
  1494. Wait.evWait = CreateEvent(NULL, //security
  1495. TRUE, //manual
  1496. FALSE, //initial state
  1497. NULL); //name
  1498. Wait.lProcessId = pCI->lProcessId;
  1499. Wait.pOwner = pCI;
  1500. Wait.WaitType = WAIT_CLIPBOARD;
  1501. rv = _WaitSomething(pCI, &Wait, dwTimeout);
  1502. if (!rv)
  1503. {
  1504. TRACE(( INFO_MESSAGE, "Clipboard received\n"));
  1505. }
  1506. CloseHandle(Wait.evWait);
  1507. exitpt:
  1508. return rv;
  1509. }
  1510. /*++
  1511. * Function:
  1512. * GetDisconnectReason
  1513. * Description:
  1514. * Retrieves, if possible, the client error box
  1515. * Arguments:
  1516. * pCI - connection context
  1517. * Return value:
  1518. * The error box message. NULL if not available
  1519. * Called by:
  1520. * SCCheck
  1521. --*/
  1522. LPCSTR GetDisconnectReason(PCONNECTINFO pCI)
  1523. {
  1524. HWND hDiscBox;
  1525. LPCSTR rv = NULL;
  1526. HWND hWnd, hwndTop, hwndNext;
  1527. char classname[128];
  1528. char caption[256];
  1529. if (!pCI)
  1530. {
  1531. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1532. rv = ERR_NULL_CONNECTINFO;
  1533. goto exitpt;
  1534. }
  1535. if (!pCI->dead)
  1536. {
  1537. rv = ERR_CLIENT_IS_DEAD;
  1538. goto exitpt;
  1539. }
  1540. if (strlen(pCI->szDiscReason))
  1541. {
  1542. rv = pCI->szDiscReason;
  1543. goto exitpt;
  1544. }
  1545. hDiscBox = _FindTopWindow(NULL, DISCONNECT_DIALOG_BOX, pCI->lProcessId);
  1546. if (!hDiscBox)
  1547. {
  1548. rv = ERR_NORMAL_EXIT;
  1549. goto exitpt;
  1550. } else {
  1551. TRACE((INFO_MESSAGE, "Found hDiscBox=0x%x", hDiscBox));
  1552. }
  1553. pCI->szDiscReason[0] = 0;
  1554. hWnd = NULL;
  1555. hwndTop = GetWindow(hDiscBox, GW_CHILD);
  1556. if (!hwndTop)
  1557. {
  1558. TRACE((INFO_MESSAGE, "GetWindow failed. hwnd=0x%x\n", hDiscBox));
  1559. goto exitpt;
  1560. }
  1561. hwndNext = hwndTop;
  1562. do {
  1563. hWnd = hwndNext;
  1564. if (!GetClassName(hWnd, classname, sizeof(classname)))
  1565. {
  1566. TRACE((INFO_MESSAGE, "GetClassName failed. hwnd=0x%x\n", hWnd));
  1567. goto nextwindow;
  1568. }
  1569. if (!GetWindowText(hWnd, caption, sizeof(caption)))
  1570. {
  1571. TRACE((INFO_MESSAGE, "GetWindowText failed. hwnd=0x%x\n"));
  1572. goto nextwindow;
  1573. }
  1574. if (!strcmp(classname, STATIC_CLASS) &&
  1575. strlen(classname) <
  1576. sizeof(pCI->szDiscReason) - strlen(pCI->szDiscReason) - 3)
  1577. {
  1578. strcat(pCI->szDiscReason, caption);
  1579. strcat(pCI->szDiscReason, "\n");
  1580. }
  1581. nextwindow:
  1582. hwndNext = GetNextWindow(hWnd, GW_HWNDNEXT);
  1583. } while (hWnd && hwndNext != hwndTop);
  1584. rv = (LPCSTR)pCI->szDiscReason;
  1585. exitpt:
  1586. return rv;
  1587. }
  1588. /*++
  1589. * Function:
  1590. * Wait4Str
  1591. * Description:
  1592. * Waits for a specific string to come from clients feedback
  1593. * Arguments:
  1594. * pCI - connection context
  1595. * lpszParam - waited string
  1596. * Return value:
  1597. * Error message, NULL on success
  1598. * Called by:
  1599. * SCCheck
  1600. --*/
  1601. LPCSTR Wait4Str(PCONNECTINFO pCI, LPCWSTR lpszParam)
  1602. {
  1603. return _Wait4Str(pCI, lpszParam, WAIT4STR_TIMEOUT, WAIT_STRING);
  1604. }
  1605. /*++
  1606. * Function:
  1607. * Wait4StrTimeout
  1608. * Description:
  1609. * Waits for a specific string to come from clients feedback
  1610. * The timeout is different than default and is specified in
  1611. * lpszParam argument, like:
  1612. * waited_string<->timeout_value
  1613. * Arguments:
  1614. * pCI - connection context
  1615. * lpszParam - waited string and timeout
  1616. * Return value:
  1617. * Error message, NULL on success
  1618. * Called by:
  1619. * SCCheck
  1620. --*/
  1621. LPCSTR Wait4StrTimeout(PCONNECTINFO pCI, LPCWSTR lpszParam)
  1622. {
  1623. WCHAR waitstr[MAX_STRING_LENGTH];
  1624. WCHAR *sep = wcsstr(lpszParam, CHAT_SEPARATOR);
  1625. DWORD dwTimeout;
  1626. LPCSTR rv = NULL;
  1627. if (!pCI)
  1628. {
  1629. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1630. rv = ERR_NULL_CONNECTINFO;
  1631. goto exitpt;
  1632. }
  1633. if (pCI->dead)
  1634. {
  1635. rv = ERR_CLIENT_IS_DEAD;
  1636. goto exitpt;
  1637. }
  1638. if (!sep)
  1639. {
  1640. TRACE((WARNING_MESSAGE,
  1641. "Wait4StrTiemout: No timeout value. Default applying\n"));
  1642. rv = Wait4Str(pCI, lpszParam);
  1643. } else {
  1644. LONG_PTR len = sep - lpszParam;
  1645. if (len > sizeof(waitstr) - 1)
  1646. len = sizeof(waitstr) - 1;
  1647. wcsncpy(waitstr, lpszParam, len);
  1648. waitstr[len] = 0;
  1649. sep += wcslen(CHAT_SEPARATOR);
  1650. dwTimeout = _wtoi(sep);
  1651. if (!dwTimeout)
  1652. {
  1653. TRACE((WARNING_MESSAGE,
  1654. "Wait4StrTiemout: No timeout value(%s). Default applying\n",
  1655. sep));
  1656. dwTimeout = WAIT4STR_TIMEOUT;
  1657. }
  1658. rv = _Wait4Str(pCI, waitstr, dwTimeout, WAIT_STRING);
  1659. }
  1660. exitpt:
  1661. return rv;
  1662. }
  1663. /*++
  1664. * Function:
  1665. * Wait4MultipleStr
  1666. * Description:
  1667. * Same as Wait4Str, but waits for several strings at once
  1668. * the strings are separated by '|' character
  1669. * Arguments:
  1670. * pCI - connection context
  1671. * lpszParam - waited strings
  1672. * Return value:
  1673. * Error message, NULL on success
  1674. * Called by:
  1675. * SCCheck
  1676. --*/
  1677. LPCSTR Wait4MultipleStr(PCONNECTINFO pCI, LPCWSTR lpszParam)
  1678. {
  1679. return _Wait4Str(pCI, lpszParam, WAIT4STR_TIMEOUT, WAIT_MSTRINGS);
  1680. }
  1681. /*++
  1682. * Function:
  1683. * Wait4MultipleStrTimeout
  1684. * Description:
  1685. * Combination between Wait4StrTimeout and Wait4MultipleStr
  1686. * Arguments:
  1687. * pCI - connection context
  1688. * lpszParam - waited strings and timeout value. Example
  1689. * - "string1|string2|...|stringN<->5000"
  1690. * Return value:
  1691. * Error message, NULL on success
  1692. * Called by:
  1693. * SCCheck
  1694. --*/
  1695. LPCSTR Wait4MultipleStrTimeout(PCONNECTINFO pCI, LPCWSTR lpszParam)
  1696. {
  1697. WCHAR waitstr[MAX_STRING_LENGTH];
  1698. WCHAR *sep = wcsstr(lpszParam, CHAT_SEPARATOR);
  1699. DWORD dwTimeout;
  1700. LPCSTR rv = NULL;
  1701. if (!pCI)
  1702. {
  1703. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1704. rv = ERR_NULL_CONNECTINFO;
  1705. goto exitpt;
  1706. }
  1707. if (pCI->dead)
  1708. {
  1709. rv = ERR_CLIENT_IS_DEAD;
  1710. goto exitpt;
  1711. }
  1712. pCI->nWait4MultipleStrResult = 0;
  1713. pCI->szWait4MultipleStrResult[0] = 0;
  1714. if (!sep)
  1715. {
  1716. TRACE((WARNING_MESSAGE,
  1717. "Wait4MultipleStrTiemout: No timeout value. Default applying"));
  1718. rv = Wait4MultipleStr(pCI, lpszParam);
  1719. } else {
  1720. LONG_PTR len = sep - lpszParam;
  1721. if (len > sizeof(waitstr) - 1)
  1722. len = sizeof(waitstr) - 1;
  1723. wcsncpy(waitstr, lpszParam, len);
  1724. waitstr[len] = 0;
  1725. sep += wcslen(CHAT_SEPARATOR);
  1726. dwTimeout = _wtoi(sep);
  1727. if (!dwTimeout)
  1728. {
  1729. TRACE((WARNING_MESSAGE,
  1730. "Wait4StrTiemout: No timeout value. Default applying"));
  1731. dwTimeout = WAIT4STR_TIMEOUT;
  1732. }
  1733. rv = _Wait4Str(pCI, waitstr, dwTimeout, WAIT_MSTRINGS);
  1734. }
  1735. exitpt:
  1736. return rv;
  1737. }
  1738. /*++
  1739. * Function:
  1740. * GetWait4MultipleStrResult
  1741. * Description:
  1742. * Retrieves the result from last Wait4MultipleStr call
  1743. * Arguments:
  1744. * pCI - connection context
  1745. * lpszParam - unused
  1746. * Return value:
  1747. * The string, NULL on error
  1748. * Called by:
  1749. * SCCheck
  1750. --*/
  1751. LPCSTR GetWait4MultipleStrResult(PCONNECTINFO pCI, LPCWSTR lpszParam)
  1752. {
  1753. LPCSTR rv = NULL;
  1754. if (!pCI)
  1755. {
  1756. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1757. rv = ERR_NULL_CONNECTINFO;
  1758. goto exitpt;
  1759. }
  1760. if (*pCI->szWait4MultipleStrResult)
  1761. rv = (LPCSTR)pCI->szWait4MultipleStrResult;
  1762. else
  1763. rv = NULL;
  1764. exitpt:
  1765. return rv;
  1766. }
  1767. /*++
  1768. * Function:
  1769. * GetFeedbackString
  1770. * Description:
  1771. * Pick a string from connection buffer or wait until
  1772. * something is received
  1773. * Arguments:
  1774. * pCI - connection context
  1775. * result - the buffer for received string
  1776. * max - the buffer size
  1777. * Return value:
  1778. * Error message, NULL on success
  1779. * Called by:
  1780. * * * * EXPORTED * * *
  1781. --*/
  1782. LPCSTR GetFeedbackString(PCONNECTINFO pCI, LPSTR result, UINT max)
  1783. {
  1784. LPCSTR rv = NULL;
  1785. int nFBpos, nFBsize ;
  1786. if (!pCI)
  1787. {
  1788. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1789. rv = ERR_NULL_CONNECTINFO;
  1790. goto exitpt;
  1791. }
  1792. if (pCI->dead)
  1793. {
  1794. rv = ERR_CLIENT_IS_DEAD;
  1795. goto exitpt;
  1796. }
  1797. // Grab the buffer pointers
  1798. EnterCriticalSection(g_lpcsGuardWaitQueue);
  1799. nFBpos = pCI->nFBend + FEEDBACK_SIZE - pCI->nFBsize;
  1800. nFBsize = pCI->nFBsize;
  1801. LeaveCriticalSection(g_lpcsGuardWaitQueue);
  1802. nFBpos %= FEEDBACK_SIZE;
  1803. if (!max)
  1804. goto exitpt;
  1805. *result = 0;
  1806. if (!nFBsize)
  1807. // Empty buffer, wait for feedback to receive
  1808. {
  1809. rv = _Wait4Str(pCI, L"", WAIT4STR_TIMEOUT, WAIT_STRING);
  1810. }
  1811. if (!rv)
  1812. // Pickup from buffer
  1813. {
  1814. UINT i;
  1815. EnterCriticalSection(g_lpcsGuardWaitQueue);
  1816. // Adjust the buffer pointers
  1817. pCI->nFBsize = pCI->nFBend + FEEDBACK_SIZE - nFBpos - 1;
  1818. pCI->nFBsize %= FEEDBACK_SIZE;
  1819. // Copy the string but watch out for overflow
  1820. if (max > wcslen(pCI->Feedback[nFBpos]) + 1)
  1821. max = wcslen(pCI->Feedback[nFBpos]);
  1822. for (i = 0; i < max; i++)
  1823. result[i] = (char)(pCI->Feedback[nFBpos][i]);
  1824. result[max] = 0;
  1825. LeaveCriticalSection(g_lpcsGuardWaitQueue);
  1826. }
  1827. exitpt:
  1828. return rv;
  1829. }
  1830. /*++
  1831. * Function:
  1832. * _SendRClxData
  1833. * Description:
  1834. * Sends request for data to the client
  1835. * Arguments:
  1836. * pCI - connection context
  1837. * pRClxData - data to send
  1838. * Return value:
  1839. * Error message, NULL on success
  1840. * Called by:
  1841. * SCGetClientScreen
  1842. --*/
  1843. LPCSTR
  1844. _SendRClxData(PCONNECTINFO pCI, PRCLXDATA pRClxData)
  1845. {
  1846. LPCSTR rv = NULL;
  1847. PRCLXCONTEXT pRClxCtx;
  1848. RCLXREQPROLOG Request;
  1849. if (!pCI)
  1850. {
  1851. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1852. rv = ERR_NULL_CONNECTINFO;
  1853. goto exitpt;
  1854. }
  1855. if (!(pCI->RClxMode))
  1856. {
  1857. TRACE((WARNING_MESSAGE, "_SendRClxData: Not in RCLX mode\n"));
  1858. rv = ERR_NULL_CONNECTINFO;
  1859. goto exitpt;
  1860. }
  1861. pRClxCtx = (PRCLXCONTEXT)pCI->hClient;
  1862. if (!pRClxCtx || pRClxCtx->hSocket == INVALID_SOCKET)
  1863. {
  1864. TRACE((ERROR_MESSAGE, "Not connected yet, RCLX context is null\n"));
  1865. rv = ERR_NULL_CONNECTINFO;
  1866. goto exitpt;
  1867. }
  1868. if (!pRClxData)
  1869. {
  1870. TRACE((ERROR_MESSAGE, "_SendRClxData: Data block is null\n"));
  1871. rv = ERR_INVALID_PARAM;
  1872. goto exitpt;
  1873. }
  1874. Request.ReqType = REQ_DATA;
  1875. Request.ReqSize = pRClxData->uiSize + sizeof(*pRClxData);
  1876. if (!RClx_SendBuffer(pRClxCtx->hSocket, &Request, sizeof(Request)))
  1877. {
  1878. rv = ERR_CLIENT_DISCONNECTED;
  1879. goto exitpt;
  1880. }
  1881. if (!RClx_SendBuffer(pRClxCtx->hSocket, pRClxData, Request.ReqSize))
  1882. {
  1883. rv = ERR_CLIENT_DISCONNECTED;
  1884. goto exitpt;
  1885. }
  1886. exitpt:
  1887. return rv;
  1888. }
  1889. /*++
  1890. * Function:
  1891. * _Wait4RClxData
  1892. * Description:
  1893. * Waits for data response from RCLX client
  1894. * Arguments:
  1895. * pCI - connection context
  1896. * dwTimeout - timeout value
  1897. * Return value:
  1898. * Error message, NULL on success
  1899. * Called by:
  1900. * SCGetClientScreen
  1901. --*/
  1902. LPCSTR
  1903. _Wait4RClxDataTimeout(PCONNECTINFO pCI, DWORD dwTimeout)
  1904. {
  1905. WAIT4STRING Wait;
  1906. LPCSTR rv = NULL;
  1907. if (!pCI)
  1908. {
  1909. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  1910. rv = ERR_NULL_CONNECTINFO;
  1911. goto exitpt;
  1912. }
  1913. if (!(pCI->RClxMode))
  1914. {
  1915. TRACE((WARNING_MESSAGE, "_Wait4RClxData: Not in RCLX mode\n"));
  1916. rv = ERR_NULL_CONNECTINFO;
  1917. goto exitpt;
  1918. }
  1919. memset(&Wait, 0, sizeof(Wait));
  1920. Wait.evWait = CreateEvent(NULL, //security
  1921. TRUE, //manual
  1922. FALSE, //initial state
  1923. NULL); //name
  1924. Wait.lProcessId = pCI->lProcessId;
  1925. Wait.pOwner = pCI;
  1926. Wait.WaitType = WAIT_DATA;
  1927. rv = _WaitSomething(pCI, &Wait, dwTimeout);
  1928. if (!rv)
  1929. {
  1930. TRACE(( INFO_MESSAGE, "RCLX data received\n"));
  1931. }
  1932. CloseHandle(Wait.evWait);
  1933. exitpt:
  1934. return rv;
  1935. }
  1936. /*++
  1937. * Function:
  1938. * _Wait4Str
  1939. * Description:
  1940. * Waits for string(s) with specified timeout
  1941. * Arguments:
  1942. * pCI - connection context
  1943. * lpszParam - waited string(s)
  1944. * dwTimeout - timeout value
  1945. * WaitType - WAIT_STRING ot WAIT_MSTRING
  1946. * Return value:
  1947. * Error message, NULL on success
  1948. * Called by:
  1949. * SCStart, Wait4Str, Wait4StrTimeout, Wait4MultipleStr
  1950. * Wait4MultipleStrTimeout, GetFeedbackString
  1951. --*/
  1952. LPCSTR _Wait4Str(PCONNECTINFO pCI,
  1953. LPCWSTR lpszParam,
  1954. DWORD dwTimeout,
  1955. WAITTYPE WaitType)
  1956. {
  1957. WAIT4STRING Wait;
  1958. int parlen, i;
  1959. LPCSTR rv = NULL;
  1960. ASSERT(pCI);
  1961. if (pCI->dead)
  1962. {
  1963. rv = ERR_CLIENT_IS_DEAD;
  1964. goto exitpt;
  1965. }
  1966. memset(&Wait, 0, sizeof(Wait));
  1967. // Check the parameter
  1968. parlen = wcslen(lpszParam);
  1969. // Copy the string
  1970. if (parlen > sizeof(Wait.waitstr)/sizeof(WCHAR)-1)
  1971. parlen = sizeof(Wait.waitstr)/sizeof(WCHAR)-1;
  1972. wcsncpy(Wait.waitstr, lpszParam, parlen);
  1973. Wait.waitstr[parlen] = 0;
  1974. Wait.strsize = parlen;
  1975. // Convert delimiters to 0s
  1976. if (WaitType == WAIT_MSTRINGS)
  1977. {
  1978. WCHAR *p = Wait.waitstr;
  1979. while((p = wcschr(p, WAIT_STR_DELIMITER)))
  1980. {
  1981. *p = 0;
  1982. p++;
  1983. }
  1984. }
  1985. Wait.evWait = CreateEvent(NULL, //security
  1986. TRUE, //manual
  1987. FALSE, //initial state
  1988. NULL); //name
  1989. if (!Wait.evWait) {
  1990. TRACE((ERROR_MESSAGE, "Couldn't create event\n"));
  1991. goto exitpt;
  1992. }
  1993. Wait.lProcessId = pCI->lProcessId;
  1994. Wait.pOwner = pCI;
  1995. Wait.WaitType = WaitType;
  1996. TRACE(( INFO_MESSAGE, "Expecting string: %S\n", Wait.waitstr));
  1997. rv = _WaitSomething(pCI, &Wait, dwTimeout);
  1998. TRACE(( INFO_MESSAGE, "String %S received\n", Wait.waitstr));
  1999. if (!rv && pCI->dead)
  2000. {
  2001. rv = ERR_CLIENT_IS_DEAD;
  2002. }
  2003. CloseHandle(Wait.evWait);
  2004. exitpt:
  2005. return rv;
  2006. }
  2007. /*++
  2008. * Function:
  2009. * _WaitSomething
  2010. * Description:
  2011. * Wait for some event: string, connect or disconnect
  2012. * Meanwhile checks for chat sequences
  2013. * Arguments:
  2014. * pCI - connection context
  2015. * pWait - the event function waits for
  2016. * dwTimeout - timeout value
  2017. * Return value:
  2018. * Error message, NULL on success
  2019. * Called by:
  2020. * Wait4Connect, Wait4Disconnect, _Wait4Str
  2021. --*/
  2022. LPCSTR
  2023. _WaitSomething(PCONNECTINFO pCI, PWAIT4STRING pWait, DWORD dwTimeout)
  2024. {
  2025. BOOL bDone = FALSE;
  2026. LPCSTR rv = NULL;
  2027. DWORD waitres;
  2028. ASSERT(pCI || pWait);
  2029. _AddToWaitQueue(pCI, pWait);
  2030. pCI->evWait4Str = pWait->evWait;
  2031. do {
  2032. if (
  2033. (waitres = WaitForMultipleObjects(
  2034. pCI->nChatNum+1,
  2035. &pCI->evWait4Str,
  2036. FALSE,
  2037. dwTimeout)) <= (pCI->nChatNum + WAIT_OBJECT_0))
  2038. {
  2039. if (waitres == WAIT_OBJECT_0)
  2040. {
  2041. bDone = TRUE;
  2042. } else {
  2043. PWAIT4STRING pNWait;
  2044. ASSERT((unsigned)pCI->nChatNum >= waitres - WAIT_OBJECT_0);
  2045. // Here we must send response messages
  2046. waitres -= WAIT_OBJECT_0 + 1;
  2047. ResetEvent(pCI->aevChatSeq[waitres]);
  2048. pNWait = _RetrieveFromWaitQByEvent(pCI->aevChatSeq[waitres]);
  2049. ASSERT(pNWait);
  2050. ASSERT(wcslen(pNWait->respstr));
  2051. TRACE((INFO_MESSAGE,
  2052. "Recieved : [%d]%S\n",
  2053. pNWait->strsize,
  2054. pNWait->waitstr ));
  2055. SCSendtextAsMsgs(pCI, (LPCWSTR)pNWait->respstr);
  2056. }
  2057. } else {
  2058. if (*(pWait->waitstr))
  2059. {
  2060. TRACE((WARNING_MESSAGE,
  2061. "Wait for \"%S\" failed: TIMEOUT\n",
  2062. pWait->waitstr));
  2063. } else {
  2064. TRACE((WARNING_MESSAGE, "Wait failed: TIMEOUT\n"));
  2065. }
  2066. rv = ERR_WAIT_FAIL_TIMEOUT;
  2067. bDone = TRUE;
  2068. }
  2069. } while(!bDone);
  2070. pCI->evWait4Str = NULL;
  2071. _RemoveFromWaitQueue(pWait);
  2072. if (!rv && pCI->dead)
  2073. rv = ERR_CLIENT_IS_DEAD;
  2074. return rv;
  2075. }
  2076. /*++
  2077. * Function:
  2078. * RegisterChat
  2079. * Description:
  2080. * This regiters a wait4str <-> sendtext pair
  2081. * so when we receive a specific string we will send a proper messages
  2082. * lpszParam is kind of: XXXXXX<->YYYYYY
  2083. * XXXXX is the waited string, YYYYY is the respond
  2084. * These command could be nested up to: MAX_WAITING_EVENTS
  2085. * Arguments:
  2086. * pCI - connection context
  2087. * lpszParam - parameter, example:
  2088. * "Connect to existing Windows NT session<->\n"
  2089. * - hit enter when this string is received
  2090. * Return value:
  2091. * Error message, NULL on success
  2092. * Called by:
  2093. * SCCheck, _Login
  2094. --*/
  2095. LPCSTR RegisterChat(PCONNECTINFO pCI, LPCWSTR lpszParam)
  2096. {
  2097. PWAIT4STRING pWait;
  2098. int parlen, i, resplen;
  2099. LPCSTR rv = NULL;
  2100. LPCWSTR resp;
  2101. if (!pCI)
  2102. {
  2103. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  2104. rv = ERR_NULL_CONNECTINFO;
  2105. goto exitpt;
  2106. }
  2107. if (!lpszParam)
  2108. {
  2109. TRACE((WARNING_MESSAGE, "Parameter is null\n"));
  2110. rv = ERR_INVALID_PARAM;
  2111. goto exitpt;
  2112. }
  2113. if (pCI->dead)
  2114. {
  2115. rv = ERR_CLIENT_IS_DEAD;
  2116. goto exitpt;
  2117. }
  2118. if (pCI->nChatNum >= MAX_WAITING_EVENTS)
  2119. {
  2120. TRACE(( WARNING_MESSAGE, "RegisterChat: too much waiting strings\n" ));
  2121. goto exitpt;
  2122. }
  2123. // Split the parameter
  2124. resp = wcsstr(lpszParam, CHAT_SEPARATOR);
  2125. // Check the strings
  2126. if (!resp)
  2127. {
  2128. TRACE(( WARNING_MESSAGE, "RegisterChat: invalid parameter\n" ));
  2129. rv = ERR_INVALID_PARAM;
  2130. goto exitpt;
  2131. }
  2132. parlen = wcslen(lpszParam) - wcslen(resp);
  2133. resp += wcslen(CHAT_SEPARATOR);
  2134. if (!parlen)
  2135. {
  2136. TRACE((WARNING_MESSAGE, "RegisterChat empty parameter\n"));
  2137. goto exitpt;
  2138. }
  2139. resplen = wcslen(resp);
  2140. if (!resplen)
  2141. {
  2142. TRACE((WARNING_MESSAGE, "RegisterChat: empty respond string\n" ));
  2143. goto exitpt;
  2144. }
  2145. // Allocate the WAIT4STRING structure
  2146. pWait = (PWAIT4STRING)malloc(sizeof(*pWait));
  2147. if (!pWait)
  2148. {
  2149. TRACE((WARNING_MESSAGE,
  2150. "RegisterChat: can't allocate %d bytes\n",
  2151. sizeof(*pWait) ));
  2152. goto exitpt;
  2153. }
  2154. memset(pWait, 0, sizeof(*pWait));
  2155. // Copy the waited string
  2156. if (parlen > sizeof(pWait->waitstr)/sizeof(WCHAR)-1)
  2157. parlen = sizeof(pWait->waitstr)/sizeof(WCHAR)-1;
  2158. wcsncpy(pWait->waitstr, lpszParam, parlen);
  2159. pWait->waitstr[parlen] = 0;
  2160. pWait->strsize = parlen;
  2161. // Copy the respond string
  2162. if (resplen > sizeof(pWait->respstr)-1)
  2163. resplen = sizeof(pWait->respstr)-1;
  2164. wcsncpy(pWait->respstr, resp, resplen);
  2165. pWait->respstr[resplen] = 0;
  2166. pWait->respsize = resplen;
  2167. pWait->evWait = CreateEvent(NULL, //security
  2168. TRUE, //manual
  2169. FALSE, //initial state
  2170. NULL); //name
  2171. if (!pWait->evWait) {
  2172. TRACE((ERROR_MESSAGE, "Couldn't create event\n"));
  2173. free (pWait);
  2174. goto exitpt;
  2175. }
  2176. pWait->lProcessId = pCI->lProcessId;
  2177. pWait->pOwner = pCI;
  2178. pWait->WaitType = WAIT_STRING;
  2179. // _AddToWaitQNoCheck(pCI, pWait);
  2180. _AddToWaitQueue(pCI, pWait);
  2181. // Add to connection info array
  2182. pCI->aevChatSeq[pCI->nChatNum] = pWait->evWait;
  2183. pCI->nChatNum++;
  2184. exitpt:
  2185. return rv;
  2186. }
  2187. // Remove a WAIT4STRING from waiting Q
  2188. // Param is the waited string
  2189. /*++
  2190. * Function:
  2191. * UnregisterChat
  2192. * Description:
  2193. * Deallocates and removes from waiting Q everithing
  2194. * from RegisterChat function
  2195. * Arguments:
  2196. * pCI - connection context
  2197. * lpszParam - waited string
  2198. * Return value:
  2199. * Error message, NULL on success
  2200. * Called by:
  2201. * SCCheck, _Login
  2202. --*/
  2203. LPCSTR UnregisterChat(PCONNECTINFO pCI, LPCWSTR lpszParam)
  2204. {
  2205. PWAIT4STRING pWait;
  2206. LPCSTR rv = NULL;
  2207. int i;
  2208. if (!pCI)
  2209. {
  2210. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  2211. rv = ERR_NULL_CONNECTINFO;
  2212. goto exitpt;
  2213. }
  2214. if (!lpszParam)
  2215. {
  2216. TRACE((WARNING_MESSAGE, "Parameter is null\n"));
  2217. rv = ERR_INVALID_PARAM;
  2218. goto exitpt;
  2219. }
  2220. pWait = _RemoveFromWaitQIndirect(pCI, lpszParam);
  2221. if (!pWait)
  2222. {
  2223. TRACE((WARNING_MESSAGE,
  2224. "UnregisterChat: can't find waiting string: %S\n",
  2225. lpszParam ));
  2226. goto exitpt;
  2227. }
  2228. i = 0;
  2229. while (i < pCI->nChatNum && pCI->aevChatSeq[i] != pWait->evWait)
  2230. i++;
  2231. ASSERT(i < pCI->nChatNum);
  2232. memmove(pCI->aevChatSeq+i,
  2233. pCI->aevChatSeq+i+1,
  2234. (pCI->nChatNum-i-1)*sizeof(pCI->aevChatSeq[0]));
  2235. pCI->nChatNum--;
  2236. CloseHandle(pWait->evWait);
  2237. free(pWait);
  2238. exitpt:
  2239. return rv;
  2240. }
  2241. /*
  2242. * Returns TRUE if the client is dead
  2243. */
  2244. PROTOCOLAPI
  2245. BOOL
  2246. SMCAPI
  2247. SCIsDead(PCONNECTINFO pCI)
  2248. {
  2249. if (!pCI)
  2250. return TRUE;
  2251. return pCI->dead;
  2252. }
  2253. /*++
  2254. * Function:
  2255. * _CloseConnectInfo
  2256. * Description:
  2257. * Clean all resources for this connection. Close the client
  2258. * Arguments:
  2259. * pCI - connection context
  2260. * Called by:
  2261. * SCDisconnect
  2262. --*/
  2263. VOID
  2264. _CloseConnectInfo(PCONNECTINFO pCI)
  2265. {
  2266. PRCLXDATACHAIN pRClxDataChain, pNext;
  2267. ASSERT(pCI);
  2268. _FlushFromWaitQ(pCI);
  2269. // Close All handles
  2270. EnterCriticalSection(g_lpcsGuardWaitQueue);
  2271. /* // not needed, the handle is already closed
  2272. if (pCI->evWait4Str)
  2273. {
  2274. CloseHandle(pCI->evWait4Str);
  2275. pCI->evWait4Str = NULL;
  2276. }
  2277. */
  2278. // Chat events are already closed by FlushFromWaitQ
  2279. // no need to close them
  2280. pCI->nChatNum = 0;
  2281. if (!pCI->RClxMode)
  2282. {
  2283. // The client was local, so we have handles opened
  2284. if (pCI->hProcess)
  2285. CloseHandle(pCI->hProcess);
  2286. if (pCI->hThread);
  2287. CloseHandle(pCI->hThread);
  2288. pCI->hProcess = pCI->hThread =NULL;
  2289. } else {
  2290. // Hmmm, RCLX mode. Then disconnect the socket
  2291. if (pCI->hClient)
  2292. RClx_EndRecv((PRCLXCONTEXT)(pCI->hClient));
  2293. pCI->hClient = NULL; // Clean the pointer
  2294. }
  2295. // Clear the clipboard handle (if any)
  2296. if (pCI->ghClipboard)
  2297. {
  2298. GlobalFree(pCI->ghClipboard);
  2299. pCI->ghClipboard = NULL;
  2300. }
  2301. // clear any recevied RCLX data
  2302. pRClxDataChain = pCI->pRClxDataChain;
  2303. while(pRClxDataChain)
  2304. {
  2305. pNext = pRClxDataChain->pNext;
  2306. free(pRClxDataChain);
  2307. pRClxDataChain = pNext;
  2308. }
  2309. LeaveCriticalSection(g_lpcsGuardWaitQueue);
  2310. if (!pCI->RClxMode)
  2311. _DeleteClientRegistry(pCI);
  2312. free(pCI);
  2313. }
  2314. /*++
  2315. * Function:
  2316. * _Login
  2317. * Description:
  2318. * Emulate login procedure
  2319. * Arguments:
  2320. * pCI - connection context
  2321. * lpszUserName - user name
  2322. * lpszPassword - password
  2323. * lpszDomain - domain name
  2324. * Return value:
  2325. * Error message, NULL on success
  2326. * Called by:
  2327. * SCConnect
  2328. --*/
  2329. LPCSTR _Login(PCONNECTINFO pCI,
  2330. LPCWSTR lpszUserName,
  2331. LPCWSTR lpszPassword,
  2332. LPCWSTR lpszDomain)
  2333. {
  2334. LPCSTR waitres;
  2335. LPCSTR rv = NULL;
  2336. WCHAR szBuff[MAX_STRING_LENGTH];
  2337. INT nLogonRetrys = 5;
  2338. UINT nLogonWaitTime;
  2339. ASSERT(pCI);
  2340. retry_logon:
  2341. _snwprintf(szBuff, MAX_STRING_LENGTH, L"%s|%s|%s",
  2342. g_strWinlogon, g_strPriorWinlogon, g_strLogonDisabled);
  2343. waitres = Wait4MultipleStr(pCI, szBuff);
  2344. if (!waitres)
  2345. {
  2346. if (pCI->nWait4MultipleStrResult == 1)
  2347. {
  2348. SCSendtextAsMsgs(pCI, g_strPriorWinlogon_Act);
  2349. waitres = Wait4Str(pCI, g_strWinlogon);
  2350. }
  2351. else if (pCI->nWait4MultipleStrResult == 2)
  2352. {
  2353. SCSendtextAsMsgs(pCI, L"\\n");
  2354. waitres = Wait4Str(pCI, g_strWinlogon);
  2355. }
  2356. }
  2357. if (waitres)
  2358. {
  2359. TRACE((WARNING_MESSAGE, "Login failed"));
  2360. rv = waitres;
  2361. goto exitpt;
  2362. }
  2363. // Hit Alt+U to go to user name field
  2364. SCSendtextAsMsgs(pCI, g_strWinlogon_Act);
  2365. SCSendtextAsMsgs(pCI, lpszUserName);
  2366. // Hit <Tab> key
  2367. Sleep(300);
  2368. SCSendtextAsMsgs(pCI, L"\\t");
  2369. SCSendtextAsMsgs(pCI, lpszPassword);
  2370. // Hit <Tab> key
  2371. Sleep(300);
  2372. SCSendtextAsMsgs(pCI, L"\\t");
  2373. SCSendtextAsMsgs(pCI, lpszDomain);
  2374. Sleep(300);
  2375. // Retry logon in case of
  2376. // 1. Winlogon is on background
  2377. // 2. Wrong username/password/domain
  2378. // 3. Other
  2379. // Hit <Enter>
  2380. SCSendtextAsMsgs(pCI, L"\\n");
  2381. nLogonWaitTime = 0;
  2382. while (!pCI->dead && !pCI->uiSessionId && nLogonWaitTime < CONNECT_TIMEOUT)
  2383. {
  2384. // Sleep with wait otherwise the chat won't work
  2385. // i.e. this is a hack
  2386. waitres = _Wait4Str(pCI, g_strLogonErrorMessage, 1000, WAIT_STRING);
  2387. if (!waitres)
  2388. // Error message received
  2389. {
  2390. SCSendtextAsMsgs(pCI, L"\\n");
  2391. Sleep(1000);
  2392. break;
  2393. }
  2394. nLogonWaitTime += 1000;
  2395. }
  2396. if (!pCI->dead && !pCI->uiSessionId)
  2397. {
  2398. TRACE((WARNING_MESSAGE, "Logon sequence failed. Retrying (%d)",
  2399. nLogonRetrys));
  2400. if (nLogonRetrys--)
  2401. goto retry_logon;
  2402. }
  2403. if (!pCI->uiSessionId)
  2404. {
  2405. // Send Enter, just in case we are not logged yet
  2406. SCSendtextAsMsgs(pCI, L"\\n");
  2407. rv = ERR_CANTLOGON;
  2408. }
  2409. exitpt:
  2410. return rv;
  2411. }
  2412. WPARAM _GetVirtualKey(INT scancode)
  2413. {
  2414. if (scancode == 29) // L Control
  2415. return VK_CONTROL;
  2416. else if (scancode == 42) // L Shift
  2417. return VK_SHIFT;
  2418. else if (scancode == 56) // L Alt
  2419. return VK_MENU;
  2420. else
  2421. return MapVirtualKey(scancode, 3);
  2422. }
  2423. /*++
  2424. * Function:
  2425. * SCSendtextAsMsgs
  2426. * Description:
  2427. * Converts a string to WM_KEYUP/KEYDOWN messages
  2428. * And sends them thru client window
  2429. * Arguments:
  2430. * pCI - connection context
  2431. * lpszString - the string to be send
  2432. * it can contain the following escape character:
  2433. * \n - Enter, \t - Tab, \^ - Esc, \& - Alt switch up/down
  2434. * \XXX - scancode XXX is down, \*XXX - scancode XXX is up
  2435. * Return value:
  2436. * Error message, NULL on success
  2437. * Called by:
  2438. * SCLogoff, SCStart, _WaitSomething, _Login
  2439. --*/
  2440. PROTOCOLAPI
  2441. LPCSTR
  2442. SMCAPI
  2443. SCSendtextAsMsgs(PCONNECTINFO pCI, LPCWSTR lpszString)
  2444. {
  2445. LPCSTR rv = NULL;
  2446. INT scancode = 0;
  2447. WPARAM vkKey;
  2448. BOOL bShiftDown = FALSE;
  2449. BOOL bAltKey = FALSE;
  2450. BOOL bCtrlKey = FALSE;
  2451. UINT uiMsg;
  2452. LPARAM lParam;
  2453. #define _SEND_KEY(_c_, _m_, _v_, _l_) {/*Sleep(40); */SCSenddata(_c_, _m_, _v_, _l_);}
  2454. if (!pCI)
  2455. {
  2456. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  2457. rv = ERR_NULL_CONNECTINFO;
  2458. goto exitpt;
  2459. }
  2460. if (!lpszString)
  2461. {
  2462. TRACE((ERROR_MESSAGE, "NULL pointer passed to SCSendtextAsMsgs"));
  2463. rv = ERR_INVALID_PARAM;
  2464. goto exitpt;
  2465. }
  2466. TRACE(( INFO_MESSAGE, "Sending: \"%S\"\n", lpszString));
  2467. for (;*lpszString; lpszString++)
  2468. {
  2469. if (*lpszString != '\\') {
  2470. try_again:
  2471. if ((scancode = OemKeyScan(*lpszString)) == 0xffffffff)
  2472. {
  2473. rv = ERR_INVALID_SCANCODE_IN_XLAT;
  2474. goto exitpt;
  2475. }
  2476. // Check the Shift key state
  2477. if ((scancode & SHIFT_DOWN) && !bShiftDown)
  2478. {
  2479. uiMsg = (bAltKey)?WM_SYSKEYDOWN:WM_KEYDOWN;
  2480. _SEND_KEY(pCI, uiMsg, VK_SHIFT,
  2481. WM_KEY_LPARAM(1, 0x2A, 0, (bAltKey)?1:0, 0, 0));
  2482. bShiftDown = TRUE;
  2483. }
  2484. else if (!(scancode & SHIFT_DOWN) && bShiftDown)
  2485. {
  2486. uiMsg = (bAltKey)?WM_SYSKEYUP:WM_KEYUP;
  2487. _SEND_KEY(pCI, uiMsg, VK_SHIFT,
  2488. WM_KEY_LPARAM(1, 0x2A, 0, (bAltKey)?1:0, 1, 1));
  2489. bShiftDown = FALSE;
  2490. }
  2491. } else {
  2492. // Non printable symbols
  2493. lpszString++;
  2494. switch(*lpszString)
  2495. {
  2496. case 'n': scancode = 0x1C; break; // Enter
  2497. case 't': scancode = 0x0F; break; // Tab
  2498. case '^': scancode = 0x01; break; // Esc
  2499. case 'p': Sleep(100); break; // Sleep for 0.1 sec
  2500. case 'P': Sleep(1000); break; // Sleep for 1 sec
  2501. case 'x': SCSendMouseClick(pCI, pCI->xRes/2, pCI->yRes/2); break;
  2502. case '&':
  2503. // Alt key
  2504. if (bAltKey)
  2505. {
  2506. _SEND_KEY(pCI, WM_KEYUP, VK_MENU,
  2507. WM_KEY_LPARAM(1, 0x38, 0, 0, 1, 1));
  2508. } else {
  2509. _SEND_KEY(pCI, WM_SYSKEYDOWN, VK_MENU,
  2510. WM_KEY_LPARAM(1, 0x38, 0, 1, 0, 0));
  2511. }
  2512. bAltKey = !bAltKey;
  2513. continue;
  2514. case '*':
  2515. lpszString ++;
  2516. if (isdigit(*lpszString))
  2517. {
  2518. INT exten;
  2519. scancode = _wtoi(lpszString);
  2520. TRACE((INFO_MESSAGE, "Scancode: %d UP\n", scancode));
  2521. vkKey = _GetVirtualKey(scancode);
  2522. uiMsg = (!bAltKey || bCtrlKey)?WM_KEYUP:WM_SYSKEYUP;
  2523. if (vkKey == VK_MENU)
  2524. bAltKey = FALSE;
  2525. else if (vkKey == VK_CONTROL)
  2526. bCtrlKey = FALSE;
  2527. else if (vkKey == VK_SHIFT)
  2528. bShiftDown = FALSE;
  2529. exten = (_IsExtendedScanCode(scancode))?1:0;
  2530. lParam = WM_KEY_LPARAM(1, scancode, exten, (bAltKey)?1:0, 1, 1);
  2531. if (uiMsg == WM_KEYUP)
  2532. {
  2533. TRACE((INFO_MESSAGE, "WM_KEYUP, 0x%x, 0x%x\n", vkKey, lParam));
  2534. } else {
  2535. TRACE((INFO_MESSAGE, "WM_SYSKEYUP, 0x%x, 0x%x\n", vkKey, lParam));
  2536. }
  2537. _SEND_KEY(pCI, uiMsg, vkKey, lParam);
  2538. while(isdigit(lpszString[1]))
  2539. lpszString++;
  2540. } else {
  2541. lpszString--;
  2542. }
  2543. continue;
  2544. break;
  2545. case 0: continue;
  2546. default:
  2547. if (isdigit(*lpszString))
  2548. {
  2549. INT exten;
  2550. scancode = _wtoi(lpszString);
  2551. TRACE((INFO_MESSAGE, "Scancode: %d DOWN\n", scancode));
  2552. vkKey = _GetVirtualKey(scancode);
  2553. if (vkKey == VK_MENU)
  2554. bAltKey = TRUE;
  2555. else if (vkKey == VK_CONTROL)
  2556. bCtrlKey = TRUE;
  2557. else if (vkKey == VK_SHIFT)
  2558. bShiftDown = TRUE;
  2559. uiMsg = (!bAltKey || bCtrlKey)?WM_KEYDOWN:WM_SYSKEYDOWN;
  2560. exten = (_IsExtendedScanCode(scancode))?1:0;
  2561. lParam = WM_KEY_LPARAM(1, scancode, exten, (bAltKey)?1:0, 0, 0);
  2562. if (uiMsg == WM_KEYDOWN)
  2563. {
  2564. TRACE((INFO_MESSAGE, "WM_KEYDOWN, 0x%x, 0x%x\n", vkKey, lParam));
  2565. } else {
  2566. TRACE((INFO_MESSAGE, "WM_SYSKEYDOWN, 0x%x, 0x%x\n", vkKey, lParam));
  2567. }
  2568. _SEND_KEY(pCI, uiMsg, vkKey, lParam);
  2569. while(isdigit(lpszString[1]))
  2570. lpszString++;
  2571. continue;
  2572. }
  2573. goto try_again;
  2574. }
  2575. }
  2576. vkKey = MapVirtualKey(scancode, 3);
  2577. // Remove flag fields
  2578. scancode &= 0xff;
  2579. uiMsg = (!bAltKey || bCtrlKey)?WM_KEYDOWN:WM_SYSKEYDOWN;
  2580. // Send the scancode
  2581. _SEND_KEY(pCI, uiMsg, vkKey,
  2582. WM_KEY_LPARAM(1, scancode, 0, (bAltKey)?1:0, 0, 0));
  2583. uiMsg = (!bAltKey || bCtrlKey)?WM_KEYUP:WM_SYSKEYUP;
  2584. _SEND_KEY(pCI, uiMsg, vkKey,
  2585. WM_KEY_LPARAM(1, scancode, 0, (bAltKey)?1:0, 1, 1));
  2586. }
  2587. // And Alt key
  2588. if (bAltKey)
  2589. _SEND_KEY(pCI, WM_KEYUP, VK_MENU,
  2590. WM_KEY_LPARAM(1, 0x38, 0, 0, 1, 1));
  2591. // Shift up
  2592. if (bShiftDown)
  2593. _SEND_KEY(pCI, WM_KEYUP, VK_LSHIFT,
  2594. WM_KEY_LPARAM(1, 0x2A, 0, 0, 1, 1));
  2595. // Ctrl key
  2596. if (bCtrlKey)
  2597. _SEND_KEY(pCI, WM_KEYUP, VK_CONTROL,
  2598. WM_KEY_LPARAM(1, 0x1D, 0, 0, 1, 1));
  2599. #undef _SEND_KEY
  2600. exitpt:
  2601. return rv;
  2602. }
  2603. /*++
  2604. * Function:
  2605. * SwitchToProcess
  2606. * Description:
  2607. * Use Alt+Tab to switch to a particular process that is already running
  2608. * Arguments:
  2609. * pCI - connection context
  2610. * lpszParam - the text in the alt-tab box that uniquely identifies the
  2611. * process we should stop at (i.e., end up switching to)
  2612. * Return value:
  2613. * Error message, NULL on success
  2614. * Called by:
  2615. * SCCheck
  2616. --*/
  2617. PROTOCOLAPI
  2618. LPCSTR
  2619. SMCAPI
  2620. SCSwitchToProcess(PCONNECTINFO pCI, LPCWSTR lpszParam)
  2621. {
  2622. #define ALT_TAB_WAIT_TIMEOUT 1000
  2623. #define MAX_APPS 20
  2624. LPCSTR rv = NULL;
  2625. LPCSTR waitres = NULL;
  2626. INT retrys = MAX_APPS;
  2627. WCHAR *wszCurrTask = 0;
  2628. if (!pCI)
  2629. {
  2630. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  2631. rv = ERR_NULL_CONNECTINFO;
  2632. goto exitpt;
  2633. }
  2634. if (pCI->dead)
  2635. {
  2636. rv = ERR_CLIENT_IS_DEAD;
  2637. goto exitpt;
  2638. }
  2639. // Wait and look for the string, before we do any switching. This makes
  2640. // sure we don't hit the string even before we hit alt-tab, and then
  2641. // end up switching to the wrong process
  2642. while (_Wait4Str(pCI, lpszParam, ALT_TAB_WAIT_TIMEOUT/5, WAIT_STRING) == 0)
  2643. ;
  2644. // Press alt down
  2645. SCSenddata(pCI, WM_KEYDOWN, 18, 540540929);
  2646. // Now loop through the list of applications (assuming there is one),
  2647. // stopping at our desired app.
  2648. do {
  2649. SCSenddata(pCI, WM_KEYDOWN, 9, 983041);
  2650. SCSenddata(pCI, WM_KEYUP, 9, -1072758783);
  2651. waitres = _Wait4Str(pCI, lpszParam, ALT_TAB_WAIT_TIMEOUT, WAIT_STRING);
  2652. retrys --;
  2653. } while (waitres && retrys);
  2654. SCSenddata(pCI, WM_KEYUP, 18, -1070071807);
  2655. rv = waitres;
  2656. exitpt:
  2657. return rv;
  2658. }
  2659. /*++
  2660. * Function:
  2661. * SCSetClientTopmost
  2662. * Description:
  2663. * Swithces the focus to this client
  2664. * Arguments:
  2665. * pCI - connection context
  2666. * lpszParam
  2667. * - "0" will remote the WS_EX_TOPMOST style
  2668. * - "non_zero" will set it as topmost window
  2669. * Return value:
  2670. * Error message, NULL on success
  2671. * Called by:
  2672. * SCCheck
  2673. --*/
  2674. PROTOCOLAPI
  2675. LPCSTR
  2676. SMCAPI
  2677. SCSetClientTopmost(
  2678. PCONNECTINFO pCI,
  2679. LPCWSTR lpszParam
  2680. )
  2681. {
  2682. LPCSTR rv = NULL;
  2683. BOOL bTop = FALSE;
  2684. if (!pCI)
  2685. {
  2686. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  2687. rv = ERR_NULL_CONNECTINFO;
  2688. goto exitpt;
  2689. }
  2690. if (pCI->dead)
  2691. {
  2692. rv = ERR_CLIENT_IS_DEAD;
  2693. goto exitpt;
  2694. }
  2695. if (pCI->RClxMode)
  2696. {
  2697. TRACE((ERROR_MESSAGE, "SetClientOnFocus not supported in RCLX mode\n"));
  2698. rv = ERR_NOTSUPPORTED;
  2699. goto exitpt;
  2700. }
  2701. if (!pCI->hClient)
  2702. {
  2703. TRACE((WARNING_MESSAGE, "Client's window handle is null\n"));
  2704. rv = ERR_INVALID_PARAM;
  2705. goto exitpt;
  2706. }
  2707. if (lpszParam)
  2708. bTop = (_wtoi(lpszParam) != 0);
  2709. else
  2710. bTop = 0;
  2711. SetWindowPos(pCI->hClient,
  2712. (bTop)?HWND_TOPMOST:HWND_NOTOPMOST,
  2713. 0,0,0,0,
  2714. SWP_NOMOVE | SWP_NOSIZE);
  2715. ShowWindow(pCI->hClient, SW_SHOWNORMAL);
  2716. if (bTop)
  2717. {
  2718. TRACE((INFO_MESSAGE, "Client is SET as topmost window\n"));
  2719. } else {
  2720. TRACE((INFO_MESSAGE, "Client is RESET as topmost window\n"));
  2721. }
  2722. exitpt:
  2723. return rv;
  2724. }
  2725. /*++
  2726. * Function:
  2727. * _SendMouseClick
  2728. * Description:
  2729. * Sends a messages for a mouse click
  2730. * Arguments:
  2731. * pCI - connection context
  2732. * xPos - mouse position
  2733. * yPos
  2734. * Return value:
  2735. * error string if fails, NULL on success
  2736. * Called by:
  2737. * * * * EXPORTED * * *
  2738. --*/
  2739. PROTOCOLAPI
  2740. LPCSTR
  2741. SMCAPI
  2742. SCSendMouseClick(
  2743. PCONNECTINFO pCI,
  2744. UINT xPos,
  2745. UINT yPos)
  2746. {
  2747. LPCSTR rv;
  2748. rv = SCSenddata(pCI, WM_LBUTTONDOWN, 0, xPos + (yPos << 16));
  2749. if (!rv)
  2750. SCSenddata(pCI, WM_LBUTTONUP, 0, xPos + (yPos << 16));
  2751. return rv;
  2752. }
  2753. /*++
  2754. * Function:
  2755. * SCSaveClientScreen
  2756. * Description:
  2757. * Saves in a file rectangle of the client's receive screen buffer
  2758. * ( aka shadow bitmap)
  2759. * Arguments:
  2760. * pCI - connection context
  2761. * left, top, right, bottom - rectangle coordinates
  2762. * if all == -1 get's the whole screen
  2763. * szFileName - file to record
  2764. * Return value:
  2765. * error string if fails, NULL on success
  2766. * Called by:
  2767. * * * * EXPORTED * * *
  2768. --*/
  2769. PROTOCOLAPI
  2770. LPCSTR
  2771. SMCAPI
  2772. SCSaveClientScreen(
  2773. PCONNECTINFO pCI,
  2774. INT left,
  2775. INT top,
  2776. INT right,
  2777. INT bottom,
  2778. LPCSTR szFileName)
  2779. {
  2780. LPCSTR rv = NULL;
  2781. PVOID pDIB = NULL;
  2782. UINT uiSize = 0;
  2783. if (!szFileName)
  2784. {
  2785. TRACE((WARNING_MESSAGE, "SCSaveClientScreen: szFileName is NULL\n"));
  2786. rv = ERR_INVALID_PARAM;
  2787. goto exitpt;
  2788. }
  2789. // leave the rest of param checking to SCGetClientScreen
  2790. rv = SCGetClientScreen(pCI, left, top, right, bottom, &uiSize, &pDIB);
  2791. if (rv)
  2792. goto exitpt;
  2793. if (!pDIB || !uiSize)
  2794. {
  2795. TRACE((ERROR_MESSAGE, "SCSaveClientScreen: failed, no data\n"));
  2796. rv = ERR_NODATA;
  2797. goto exitpt;
  2798. }
  2799. if (!SaveDIB(pDIB, szFileName))
  2800. {
  2801. TRACE((ERROR_MESSAGE, "SCSaveClientScreen: save failed\n"));
  2802. rv = ERR_NODATA;
  2803. goto exitpt;
  2804. }
  2805. exitpt:
  2806. if (pDIB)
  2807. free(pDIB);
  2808. return rv;
  2809. }
  2810. /*++
  2811. * Function:
  2812. * SCGetClientScreen
  2813. * Description:
  2814. * Gets rectangle of the client's receive screen buffer
  2815. * ( aka shadow bitmap)
  2816. * Arguments:
  2817. * pCI - connection context
  2818. * left, top, right, bottom - rectangle coordinates
  2819. * if all == -1 get's the whole screen
  2820. * ppDIB - pointer to the received DIB
  2821. * puiSize - size of allocated data in ppDIB
  2822. *
  2823. * !!!!! DON'T FORGET to free() THAT MEMORY !!!!!
  2824. *
  2825. * Return value:
  2826. * error string if fails, NULL on success
  2827. * Called by:
  2828. * SCSaveClientScreen
  2829. * * * * EXPORTED * * *
  2830. --*/
  2831. PROTOCOLAPI
  2832. LPCSTR
  2833. SMCAPI
  2834. SCGetClientScreen(
  2835. PCONNECTINFO pCI,
  2836. INT left,
  2837. INT top,
  2838. INT right,
  2839. INT bottom,
  2840. UINT *puiSize,
  2841. PVOID *ppDIB)
  2842. {
  2843. LPCSTR rv;
  2844. PRCLXDATA pRClxData;
  2845. PREQBITMAP pReqBitmap;
  2846. PRCLXDATACHAIN pIter, pPrev, pNext;
  2847. PRCLXDATACHAIN pRClxDataChain = NULL;
  2848. if (!pCI)
  2849. {
  2850. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  2851. rv = ERR_NULL_CONNECTINFO;
  2852. goto exitpt;
  2853. }
  2854. if (pCI->dead)
  2855. {
  2856. rv = ERR_CLIENT_IS_DEAD;
  2857. goto exitpt;
  2858. }
  2859. if (!pCI->RClxMode)
  2860. {
  2861. TRACE((WARNING_MESSAGE, "SCGetClientScreen is not supported in non-RCLX mode\n"));
  2862. rv = ERR_NOTSUPPORTED;
  2863. goto exitpt;
  2864. }
  2865. if (!ppDIB || !puiSize)
  2866. {
  2867. TRACE((WARNING_MESSAGE, "ppDIB and/or puiSize parameter is NULL\n"));
  2868. rv = ERR_INVALID_PARAM;
  2869. goto exitpt;
  2870. }
  2871. // Remove all recieved DATA_BITMAP from the recieve buffer
  2872. EnterCriticalSection(g_lpcsGuardWaitQueue);
  2873. {
  2874. pIter = pCI->pRClxDataChain;
  2875. pPrev = NULL;
  2876. while (pIter)
  2877. {
  2878. pNext = pIter->pNext;
  2879. if (pIter->RClxData.uiType == DATA_BITMAP)
  2880. {
  2881. // dispose this entry
  2882. if (pPrev)
  2883. pPrev->pNext = pIter->pNext;
  2884. else
  2885. pCI->pRClxDataChain = pIter->pNext;
  2886. if (!pIter->pNext)
  2887. pCI->pRClxLastDataChain = pPrev;
  2888. free(pIter);
  2889. } else
  2890. pPrev = pIter;
  2891. pIter = pNext;
  2892. }
  2893. }
  2894. LeaveCriticalSection(g_lpcsGuardWaitQueue);
  2895. pRClxData = alloca(sizeof(*pRClxData) + sizeof(*pReqBitmap));
  2896. pRClxData->uiType = DATA_BITMAP;
  2897. pRClxData->uiSize = sizeof(*pReqBitmap);
  2898. pReqBitmap = (PREQBITMAP)pRClxData->Data;
  2899. pReqBitmap->left = left;
  2900. pReqBitmap->top = top;
  2901. pReqBitmap->right = right;
  2902. pReqBitmap->bottom = bottom;
  2903. TRACE((INFO_MESSAGE, "Getting client's DIB (%d, %d, %d, %d)\n", left, top, right, bottom));
  2904. rv = _SendRClxData(pCI, pRClxData);
  2905. if (rv)
  2906. goto exitpt;
  2907. do {
  2908. rv = _Wait4RClxDataTimeout(pCI, WAIT4STR_TIMEOUT);
  2909. if (rv)
  2910. goto exitpt;
  2911. if (!pCI->pRClxDataChain)
  2912. {
  2913. TRACE((ERROR_MESSAGE, "RClxData is not received\n"));
  2914. rv = ERR_WAIT_FAIL_TIMEOUT;
  2915. goto exitpt;
  2916. }
  2917. EnterCriticalSection(g_lpcsGuardWaitQueue);
  2918. // Get any received DATA_BITMAP
  2919. {
  2920. pIter = pCI->pRClxDataChain;
  2921. pPrev = NULL;
  2922. while (pIter)
  2923. {
  2924. pNext = pIter->pNext;
  2925. if (pIter->RClxData.uiType == DATA_BITMAP)
  2926. {
  2927. // dispose this entry from the chain
  2928. if (pPrev)
  2929. pPrev->pNext = pIter->pNext;
  2930. else
  2931. pCI->pRClxDataChain = pIter->pNext;
  2932. if (!pIter->pNext)
  2933. pCI->pRClxLastDataChain = pPrev;
  2934. goto entry_is_found;
  2935. } else
  2936. pPrev = pIter;
  2937. pIter = pNext;
  2938. }
  2939. entry_is_found:
  2940. pRClxDataChain = (pIter && pIter->RClxData.uiType == DATA_BITMAP)?
  2941. pIter:NULL;
  2942. }
  2943. LeaveCriticalSection(g_lpcsGuardWaitQueue);
  2944. } while (!pRClxDataChain && !pCI->dead);
  2945. if (!pRClxDataChain)
  2946. {
  2947. TRACE((WARNING_MESSAGE, "SCGetClientScreen: client died\n"));
  2948. goto exitpt;
  2949. }
  2950. *ppDIB = malloc(pRClxDataChain->RClxData.uiSize);
  2951. if (!(*ppDIB))
  2952. {
  2953. TRACE((WARNING_MESSAGE, "Can't allocate %d bytes\n",
  2954. pRClxDataChain->RClxData.uiSize));
  2955. rv = ERR_ALLOCATING_MEMORY;
  2956. goto exitpt;
  2957. }
  2958. memcpy(*ppDIB,
  2959. pRClxDataChain->RClxData.Data,
  2960. pRClxDataChain->RClxData.uiSize);
  2961. *puiSize = pRClxDataChain->RClxData.uiSize;
  2962. exitpt:
  2963. if (pRClxDataChain)
  2964. free(pRClxDataChain);
  2965. return rv;
  2966. }
  2967. /*++
  2968. * Function:
  2969. * SCSendVCData
  2970. * Description:
  2971. * Sends data to a virtual channel
  2972. * Arguments:
  2973. * pCI - connection context
  2974. * szVCName - the virtual channel name
  2975. * pData - data
  2976. * uiSize - data size
  2977. * Return value:
  2978. * error string if fails, NULL on success
  2979. * Called by:
  2980. * * * * EXPORTED * * *
  2981. --*/
  2982. PROTOCOLAPI
  2983. LPCSTR
  2984. SMCAPI
  2985. SCSendVCData(
  2986. PCONNECTINFO pCI,
  2987. LPCSTR szVCName,
  2988. PVOID pData,
  2989. UINT uiSize
  2990. )
  2991. {
  2992. LPCSTR rv;
  2993. PRCLXDATA pRClxData = NULL;
  2994. CHAR *szName2Send;
  2995. PVOID pData2Send;
  2996. UINT uiPacketSize;
  2997. if (!pCI)
  2998. {
  2999. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  3000. rv = ERR_NULL_CONNECTINFO;
  3001. goto exitpt;
  3002. }
  3003. if (pCI->dead)
  3004. {
  3005. rv = ERR_CLIENT_IS_DEAD;
  3006. goto exitpt;
  3007. }
  3008. if (!pCI->RClxMode)
  3009. {
  3010. TRACE((WARNING_MESSAGE, "SCSendVCData is not supported in non-RCLXmode\n"));
  3011. rv = ERR_NOTSUPPORTED;
  3012. goto exitpt;
  3013. }
  3014. if (!pData || !uiSize)
  3015. {
  3016. TRACE((WARNING_MESSAGE, "pData and/or uiSize parameter are NULL\n"));
  3017. rv = ERR_INVALID_PARAM;
  3018. goto exitpt;
  3019. }
  3020. if (strlen(szVCName) > MAX_VCNAME_LEN - 1)
  3021. {
  3022. TRACE((WARNING_MESSAGE, "channel name too long\n"));
  3023. rv = ERR_INVALID_PARAM;
  3024. goto exitpt;
  3025. }
  3026. uiPacketSize = sizeof(*pRClxData) + MAX_VCNAME_LEN + uiSize;
  3027. pRClxData = malloc(uiPacketSize);
  3028. if (!pRClxData)
  3029. {
  3030. TRACE((ERROR_MESSAGE, "SCSendVCData: can't allocate %d bytes\n",
  3031. uiPacketSize));
  3032. rv = ERR_ALLOCATING_MEMORY;
  3033. goto exitpt;
  3034. }
  3035. pRClxData->uiType = DATA_VC;
  3036. pRClxData->uiSize = uiPacketSize - sizeof(*pRClxData);
  3037. szName2Send = (CHAR *)pRClxData->Data;
  3038. strcpy(szName2Send, szVCName);
  3039. pData2Send = szName2Send + MAX_VCNAME_LEN;
  3040. memcpy(pData2Send, pData, uiSize);
  3041. rv = _SendRClxData(pCI, pRClxData);
  3042. exitpt:
  3043. if (pRClxData)
  3044. free(pRClxData);
  3045. return rv;
  3046. }
  3047. /*++
  3048. * Function:
  3049. * SCRecvVCData
  3050. * Description:
  3051. * Receives data from virtual channel
  3052. * Arguments:
  3053. * pCI - connection context
  3054. * szVCName - the virtual channel name
  3055. * ppData - data pointer
  3056. *
  3057. * !!!!! DON'T FORGET to free() THAT MEMORY !!!!!
  3058. *
  3059. * puiSize - pointer to the data size
  3060. * Return value:
  3061. * error string if fails, NULL on success
  3062. * Called by:
  3063. * * * * EXPORTED * * *
  3064. --*/
  3065. PROTOCOLAPI
  3066. LPCSTR
  3067. SMCAPI
  3068. SCRecvVCData(
  3069. PCONNECTINFO pCI,
  3070. LPCSTR szVCName,
  3071. PVOID pData,
  3072. UINT uiBlockSize,
  3073. UINT *puiBytesRead
  3074. )
  3075. {
  3076. LPCSTR rv;
  3077. LPSTR szRecvVCName;
  3078. PVOID pChanData;
  3079. PRCLXDATACHAIN pIter, pPrev, pNext;
  3080. PRCLXDATACHAIN pRClxDataChain = NULL;
  3081. UINT uiBytesRead = 0;
  3082. BOOL bBlockFree = FALSE;
  3083. if (!pCI)
  3084. {
  3085. TRACE((WARNING_MESSAGE, "Connection info is null\n"));
  3086. rv = ERR_NULL_CONNECTINFO;
  3087. goto exitpt;
  3088. }
  3089. if (pCI->dead)
  3090. {
  3091. rv = ERR_CLIENT_IS_DEAD;
  3092. goto exitpt;
  3093. }
  3094. if (!pCI->RClxMode)
  3095. {
  3096. TRACE((WARNING_MESSAGE, "SCRecvVCData is not supported in non-RCLXmode\n"));
  3097. rv = ERR_NOTSUPPORTED;
  3098. goto exitpt;
  3099. }
  3100. if (!pData || !uiBlockSize || !puiBytesRead)
  3101. {
  3102. TRACE((WARNING_MESSAGE, "Invalid parameters\n"));
  3103. rv = ERR_INVALID_PARAM;
  3104. goto exitpt;
  3105. }
  3106. if (strlen(szVCName) > MAX_VCNAME_LEN - 1)
  3107. {
  3108. TRACE((WARNING_MESSAGE, "channel name too long\n"));
  3109. rv = ERR_INVALID_PARAM;
  3110. goto exitpt;
  3111. }
  3112. // Extract data entry from this channel
  3113. do {
  3114. if (!pCI->pRClxDataChain)
  3115. {
  3116. rv = _Wait4RClxDataTimeout(pCI, WAIT4STR_TIMEOUT);
  3117. if (rv)
  3118. goto exitpt;
  3119. }
  3120. EnterCriticalSection(g_lpcsGuardWaitQueue);
  3121. // Search for data from this channel
  3122. {
  3123. pIter = pCI->pRClxDataChain;
  3124. pPrev = NULL;
  3125. while (pIter)
  3126. {
  3127. pNext = pIter->pNext;
  3128. if (pIter->RClxData.uiType == DATA_VC &&
  3129. !_stricmp(pIter->RClxData.Data, szVCName))
  3130. {
  3131. if (pIter->RClxData.uiSize - pIter->uiOffset - MAX_VCNAME_LEN <= uiBlockSize)
  3132. {
  3133. // will read the whole block
  3134. // dispose this entry
  3135. if (pPrev)
  3136. pPrev->pNext = pIter->pNext;
  3137. else
  3138. pCI->pRClxDataChain = pIter->pNext;
  3139. if (!pIter->pNext)
  3140. pCI->pRClxLastDataChain = pPrev;
  3141. bBlockFree = TRUE;
  3142. }
  3143. goto entry_is_found;
  3144. } else
  3145. pPrev = pIter;
  3146. pIter = pNext;
  3147. }
  3148. entry_is_found:
  3149. pRClxDataChain = (pIter && pIter->RClxData.uiType == DATA_VC)?
  3150. pIter:NULL;
  3151. }
  3152. LeaveCriticalSection(g_lpcsGuardWaitQueue);
  3153. } while (!pRClxDataChain && !pCI->dead);
  3154. ASSERT(pRClxDataChain->RClxData.uiType == DATA_VC);
  3155. szRecvVCName = pRClxDataChain->RClxData.Data;
  3156. if (_stricmp(szRecvVCName, szVCName))
  3157. {
  3158. TRACE((ERROR_MESSAGE, "SCRecvVCData: received from different channel: %s\n", szRecvVCName));
  3159. ASSERT(0);
  3160. }
  3161. pChanData = (BYTE *)(pRClxDataChain->RClxData.Data) +
  3162. pRClxDataChain->uiOffset + MAX_VCNAME_LEN;
  3163. uiBytesRead = pRClxDataChain->RClxData.uiSize -
  3164. pRClxDataChain->uiOffset - MAX_VCNAME_LEN;
  3165. if (uiBytesRead > uiBlockSize)
  3166. uiBytesRead = uiBlockSize;
  3167. memcpy(pData, pChanData, uiBytesRead);
  3168. pRClxDataChain->uiOffset += uiBytesRead;
  3169. rv = NULL;
  3170. exitpt:
  3171. if (pRClxDataChain && bBlockFree)
  3172. {
  3173. ASSERT(pRClxDataChain->uiOffset + MAX_VCNAME_LEN == pRClxDataChain->RClxData.uiSize);
  3174. free(pRClxDataChain);
  3175. }
  3176. if (puiBytesRead)
  3177. {
  3178. *puiBytesRead = uiBytesRead;
  3179. TRACE((INFO_MESSAGE, "SCRecvVCData: %d bytes read\n", uiBytesRead));
  3180. }
  3181. return rv;
  3182. }
  3183. /*++
  3184. * Function:
  3185. * _EnumWindowsProc
  3186. * Description:
  3187. * Used to find a specific window
  3188. * Arguments:
  3189. * hWnd - current enumerated window handle
  3190. * lParam - pointer to SEARCHWND passed from
  3191. * _FindTopWindow
  3192. * Return value:
  3193. * TRUE on success but window is not found
  3194. * FALSE if the window is found
  3195. * Called by:
  3196. * _FindTopWindow thru EnumWindows
  3197. --*/
  3198. BOOL CALLBACK _EnumWindowsProc( HWND hWnd, LPARAM lParam )
  3199. {
  3200. TCHAR classname[128];
  3201. TCHAR caption[128];
  3202. BOOL rv = TRUE;
  3203. DWORD dwProcessId;
  3204. LONG_PTR lProcessId;
  3205. PSEARCHWND pSearch = (PSEARCHWND)lParam;
  3206. if (pSearch->szClassName &&
  3207. !GetClassName(hWnd, classname, sizeof(classname)))
  3208. {
  3209. goto exitpt;
  3210. }
  3211. if (pSearch->szCaption && !GetWindowText(hWnd, caption, sizeof(caption)))
  3212. {
  3213. goto exitpt;
  3214. }
  3215. GetWindowThreadProcessId(hWnd, &dwProcessId);
  3216. lProcessId = dwProcessId;
  3217. if (
  3218. (!pSearch->szClassName || ! // Check for classname
  3219. #ifdef UNICODE
  3220. wcscmp
  3221. #else
  3222. strcmp
  3223. #endif
  3224. (classname, pSearch->szClassName))
  3225. &&
  3226. (!pSearch->szCaption || !
  3227. #ifdef UNICODE
  3228. wcscmp
  3229. #else
  3230. strcmp
  3231. #endif
  3232. (caption, pSearch->szCaption))
  3233. &&
  3234. lProcessId == pSearch->lProcessId)
  3235. {
  3236. ((PSEARCHWND)lParam)->hWnd = hWnd;
  3237. rv = FALSE;
  3238. }
  3239. exitpt:
  3240. return rv;
  3241. }
  3242. /*++
  3243. * Function:
  3244. * _FindTopWindow
  3245. * Description:
  3246. * Find specific window by classname and/or caption and/or process Id
  3247. * Arguments:
  3248. * classname - class name to search for, NULL ignore
  3249. * caption - caption to search for, NULL ignore
  3250. * dwProcessId - process Id, 0 ignore
  3251. * Return value:
  3252. * window handle found, NULL otherwise
  3253. * Called by:
  3254. * SCConnect, SCDisconnect, GetDisconnectResult
  3255. --*/
  3256. HWND _FindTopWindow(LPTSTR classname, LPTSTR caption, LONG_PTR lProcessId)
  3257. {
  3258. SEARCHWND search;
  3259. search.szClassName = classname;
  3260. search.szCaption = caption;
  3261. search.hWnd = NULL;
  3262. search.lProcessId = lProcessId;
  3263. EnumWindows(_EnumWindowsProc, (LPARAM)&search);
  3264. return search.hWnd;
  3265. }
  3266. /*++
  3267. * Function:
  3268. * _FindWindow
  3269. * Description:
  3270. * Find child window by caption and/or classname
  3271. * Arguments:
  3272. * hwndParent - the parent window handle
  3273. * srchcaption - caption to search for, NULL - ignore
  3274. * srchclass - class name to search for, NULL - ignore
  3275. * Return value:
  3276. * window handle found, NULL otherwise
  3277. * Called by:
  3278. * SCConnect
  3279. --*/
  3280. HWND _FindWindow(HWND hwndParent, LPTSTR srchcaption, LPTSTR srchclass)
  3281. {
  3282. HWND hWnd, hwndTop, hwndNext;
  3283. BOOL bFound;
  3284. TCHAR classname[128];
  3285. TCHAR caption[128];
  3286. hWnd = NULL;
  3287. hwndTop = GetWindow(hwndParent, GW_CHILD);
  3288. if (!hwndTop)
  3289. {
  3290. TRACE((INFO_MESSAGE, "GetWindow failed. hwnd=0x%x\n", hwndParent));
  3291. goto exiterr;
  3292. }
  3293. bFound = FALSE;
  3294. hwndNext = hwndTop;
  3295. do {
  3296. hWnd = hwndNext;
  3297. if (srchclass && !GetClassName(hWnd, classname, sizeof(classname)))
  3298. {
  3299. TRACE((INFO_MESSAGE, "GetClassName failed. hwnd=0x%x\n"));
  3300. goto nextwindow;
  3301. }
  3302. if (srchcaption && !GetWindowText(hWnd, caption, sizeof(caption)))
  3303. {
  3304. TRACE((INFO_MESSAGE, "GetWindowText failed. hwnd=0x%x\n"));
  3305. goto nextwindow;
  3306. }
  3307. if (
  3308. (!srchclass || !
  3309. #ifdef UNICODE
  3310. wcscmp
  3311. #else
  3312. strcmp
  3313. #endif
  3314. (classname, srchclass))
  3315. &&
  3316. (!srchcaption || !
  3317. #ifdef UNICODE
  3318. wcscmp
  3319. #else
  3320. strcmp
  3321. #endif
  3322. (caption, srchcaption))
  3323. )
  3324. bFound = TRUE;
  3325. nextwindow:
  3326. hwndNext = GetNextWindow(hWnd, GW_HWNDNEXT);
  3327. } while (hWnd && hwndNext != hwndTop && !bFound);
  3328. if (!bFound) goto exiterr;
  3329. return hWnd;
  3330. exiterr:
  3331. return NULL;
  3332. }
  3333. BOOL
  3334. _IsExtendedScanCode(INT scancode)
  3335. {
  3336. static BYTE extscans[] = \
  3337. {28, 29, 53, 55, 56, 71, 72, 73, 75, 77, 79, 80, 81, 82, 83, 87, 88};
  3338. INT idx;
  3339. for (idx = 0; idx < sizeof(extscans); idx++)
  3340. {
  3341. if (scancode == (INT)extscans[idx])
  3342. return TRUE;
  3343. }
  3344. return FALSE;
  3345. }
  3346. PROTOCOLAPI
  3347. BOOL
  3348. SMCAPI
  3349. SCOpenClipboard(HWND hwnd)
  3350. {
  3351. return OpenClipboard(hwnd);
  3352. }
  3353. PROTOCOLAPI
  3354. BOOL
  3355. SMCAPI
  3356. SCCloseClipboard(VOID)
  3357. {
  3358. return CloseClipboard();
  3359. }