3891 lines
98 KiB
C
3891 lines
98 KiB
C
/*++
|
|
* File name:
|
|
* scfuncs.c
|
|
* Contents:
|
|
* Functions exported to smclient intepreter
|
|
*
|
|
* Copyright (C) 1998-1999 Microsoft Corp.
|
|
--*/
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <malloc.h>
|
|
#include <process.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <io.h>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <winsock.h>
|
|
|
|
#include "tclient.h"
|
|
|
|
#define PROTOCOLAPI
|
|
#include "protocol.h"
|
|
|
|
#include "gdata.h"
|
|
#include "queues.h"
|
|
#include "misc.h"
|
|
#include "rclx.h"
|
|
#include "..\..\bmplib\bmplib.h"
|
|
|
|
// This structure is used by _FindTopWindow
|
|
typedef struct _SEARCHWND {
|
|
TCHAR *szClassName; // The class name of searched window,
|
|
// NULL - ignore
|
|
TCHAR *szCaption; // Window caption, NULL - ignore
|
|
LONG_PTR lProcessId; // Process Id of the owner, 0 - ignore
|
|
HWND hWnd; // Found window handle
|
|
} SEARCHWND, *PSEARCHWND;
|
|
|
|
/*
|
|
* Internal exported functions
|
|
*/
|
|
LPCSTR Wait4Str(PCONNECTINFO, LPCWSTR);
|
|
LPCSTR Wait4StrTimeout(PCONNECTINFO, LPCWSTR);
|
|
LPCSTR Wait4MultipleStr(PCONNECTINFO, LPCWSTR);
|
|
LPCSTR Wait4MultipleStrTimeout(PCONNECTINFO, LPCWSTR);
|
|
LPCSTR GetWait4MultipleStrResult(PCONNECTINFO, LPCWSTR);
|
|
LPCSTR GetFeedbackString(PCONNECTINFO, LPSTR result, UINT max);
|
|
LPCSTR Wait4Disconnect(PCONNECTINFO);
|
|
LPCSTR Wait4Connect(PCONNECTINFO);
|
|
LPCSTR RegisterChat(PCONNECTINFO pCI, LPCWSTR lpszParam);
|
|
LPCSTR UnregisterChat(PCONNECTINFO pCI, LPCWSTR lpszParam);
|
|
LPCSTR GetDisconnectReason(PCONNECTINFO pCI);
|
|
PROTOCOLAPI
|
|
LPCSTR SMCAPI SCSendtextAsMsgs(PCONNECTINFO, LPCWSTR);
|
|
PROTOCOLAPI
|
|
LPCSTR SMCAPI SCSwitchToProcess(PCONNECTINFO pCI, LPCWSTR lpszParam);
|
|
PROTOCOLAPI
|
|
LPCSTR SMCAPI SCSetClientTopmost(PCONNECTINFO pCI, LPCWSTR lpszParam);
|
|
PROTOCOLAPI
|
|
LPCSTR SMCAPI SCSendMouseClick(PCONNECTINFO pCI, UINT xPos, UINT yPos);
|
|
PROTOCOLAPI
|
|
LPCSTR SMCAPI SCGetClientScreen(PCONNECTINFO pCI, INT left, INT top, INT right, INT bottom, UINT *puiSize, PVOID *ppDIB);
|
|
PROTOCOLAPI
|
|
LPCSTR SMCAPI SCSaveClientScreen(PCONNECTINFO pCI, INT left, INT top, INT right, INT bottom, LPCSTR szFileName);
|
|
|
|
/*
|
|
* Intenal functions definition
|
|
*/
|
|
LPCSTR _Wait4ConnectTimeout(PCONNECTINFO pCI, DWORD dwTimeout);
|
|
LPCSTR _Wait4ClipboardTimeout(PCONNECTINFO pCI, DWORD dwTimeout);
|
|
LPCSTR _SendRClxData(PCONNECTINFO pCI, PRCLXDATA pRClxData);
|
|
LPCSTR _Wait4RClxDataTimeout(PCONNECTINFO pCI, DWORD dwTimeout);
|
|
LPCSTR _Wait4Str(PCONNECTINFO, LPCWSTR, DWORD dwTimeout, WAITTYPE);
|
|
LPCSTR _WaitSomething(PCONNECTINFO pCI, PWAIT4STRING pWait, DWORD dwTimeout);
|
|
VOID _CloseConnectInfo(PCONNECTINFO);
|
|
LPCSTR _Login(PCONNECTINFO, LPCWSTR, LPCWSTR, LPCWSTR);
|
|
HWND _FindTopWindow(LPTSTR, LPTSTR, LONG_PTR);
|
|
HWND _FindWindow(HWND, LPTSTR, LPTSTR);
|
|
BOOL _IsExtendedScanCode(INT scancode);
|
|
|
|
/*
|
|
* Clipboard help functions (clputil.c)
|
|
*/
|
|
VOID
|
|
Clp_GetClipboardData(
|
|
UINT format,
|
|
HGLOBAL hClipData,
|
|
INT *pnClipDataSize,
|
|
HGLOBAL *phNewData);
|
|
|
|
BOOL
|
|
Clp_SetClipboardData(
|
|
UINT formatID,
|
|
HGLOBAL hClipData,
|
|
INT nClipDataSize,
|
|
BOOL *pbFreeHandle);
|
|
|
|
UINT
|
|
Clp_GetClipboardFormat(LPCSTR szFormatLookup);
|
|
|
|
BOOL
|
|
Clp_EmptyClipboard(VOID);
|
|
|
|
BOOL
|
|
Clp_CheckEmptyClipboard(VOID);
|
|
|
|
UINT
|
|
_GetKnownClipboardFormatIDByName(LPCSTR szFormatName);
|
|
|
|
/*++
|
|
* Function:
|
|
* SCInit
|
|
* Description:
|
|
* Called by smclient after the library is loaded.
|
|
* Passes trace routine
|
|
* Arguments:
|
|
* pInitData - contains a trace routine
|
|
* Called by:
|
|
* !smclient
|
|
--*/
|
|
PROTOCOLAPI
|
|
VOID
|
|
SMCAPI
|
|
SCInit(SCINITDATA *pInitData)
|
|
{
|
|
g_pfnPrintMessage = pInitData->pfnPrintMessage;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* SCConnectEx
|
|
* Description:
|
|
* Called by smclient when connect command is interpreted
|
|
* Arguments:
|
|
* lpszServerName - server to connect to
|
|
* lpszUserName - login user name. Empty string means no login
|
|
* lpszPassword - login password
|
|
* lpszDomain - login domain, empty string means login to a domain
|
|
* the same as lpszServerName
|
|
* xRes, yRes - clients resolution, 0x0 - default
|
|
* ConnectFlags -
|
|
* - low speed (compression) option
|
|
* - cache the bitmaps to the disc option
|
|
* - connection context allocated in this function
|
|
* Return value:
|
|
* Error message. NULL on success
|
|
* Called by:
|
|
* SCConnect
|
|
--*/
|
|
PROTOCOLAPI
|
|
LPCSTR
|
|
SMCAPI
|
|
SCConnectEx(
|
|
LPCWSTR lpszServerName,
|
|
LPCWSTR lpszUserName,
|
|
LPCWSTR lpszPassword,
|
|
LPCWSTR lpszDomain,
|
|
LPCWSTR lpszShell,
|
|
IN const int xRes,
|
|
IN const int yRes,
|
|
IN const int ConnectionFlags,
|
|
PCONNECTINFO *ppCI)
|
|
{
|
|
HWND hDialog, hClient, hConnect;
|
|
HWND hContainer, hInput, hOutput;
|
|
STARTUPINFO si;
|
|
PROCESS_INFORMATION procinfo;
|
|
LPCSTR rv = NULL;
|
|
int trys;
|
|
CHAR szCommandLine[MAX_STRING_LENGTH];
|
|
LPCSTR szDiscon;
|
|
UINT xxRes, yyRes;
|
|
|
|
// Correct the resolution
|
|
if (xRes >= 1600 && yRes >= 1200) {xxRes = 1600; yyRes = 1200;}
|
|
else if (xRes >= 1280 && yRes >= 1024) {xxRes = 1280; yyRes = 1024;}
|
|
else if (xRes >= 1024 && yRes >= 768) {xxRes = 1024; yyRes = 768;}
|
|
else if (xRes >= 800 && yRes >= 600) {xxRes = 800; yyRes = 600;}
|
|
else {xxRes = 640; yyRes = 480;}
|
|
|
|
*ppCI = NULL;
|
|
|
|
for (trys = 60; trys && !g_hWindow; trys--)
|
|
Sleep(1000);
|
|
|
|
if (!g_hWindow)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "Panic !!! Feedback window is not created\n"));
|
|
rv = ERR_WAIT_FAIL_TIMEOUT;
|
|
goto exitpt;
|
|
}
|
|
|
|
*ppCI = (PCONNECTINFO)malloc(sizeof(**ppCI));
|
|
|
|
if (!*ppCI)
|
|
{
|
|
TRACE((ERROR_MESSAGE,
|
|
"Couldn't allocate %d bytes memory\n",
|
|
sizeof(**ppCI)));
|
|
rv = ERR_ALLOCATING_MEMORY;
|
|
goto exitpt;
|
|
}
|
|
memset(*ppCI, 0, sizeof(**ppCI));
|
|
|
|
(*ppCI)->OwnerThreadId = GetCurrentThreadId();
|
|
|
|
// Check in what mode the client will be executed
|
|
// if the server name starts with '\'
|
|
// then tclient.dll will wait until some remote client
|
|
// is connected (aka RCLX mode)
|
|
// otherwise start the client on the same machine
|
|
// running tclient.dll (smclient)
|
|
if (*lpszServerName != L'\\')
|
|
{
|
|
// This is local mode, start the RDP client process
|
|
FillMemory(&si, sizeof(si), 0);
|
|
si.cb = sizeof(si);
|
|
si.wShowWindow = SW_SHOWMINIMIZED;
|
|
|
|
_SetClientRegistry(lpszServerName,
|
|
lpszShell,
|
|
xxRes, yyRes,
|
|
ConnectionFlags);
|
|
|
|
_snprintf(szCommandLine, sizeof(szCommandLine),
|
|
#ifdef _WIN64
|
|
"%s /CLXDLL=CLXTSHAR.DLL /CLXCMDLINE=%s%I64d " REG_FORMAT,
|
|
#else // !_WIN64
|
|
"%s /CLXDLL=CLXTSHAR.DLL /CLXCMDLINE=%s%d " REG_FORMAT,
|
|
#endif // _WIN64
|
|
g_strClientImg, _HWNDOPT,
|
|
g_hWindow, GetCurrentProcessId(), GetCurrentThreadId());
|
|
|
|
(*ppCI)->dead = FALSE;
|
|
|
|
_AddToClientQ(*ppCI);
|
|
|
|
if (!CreateProcessA(NULL,
|
|
szCommandLine,
|
|
NULL, // Security attribute for process
|
|
NULL, // Security attribute for thread
|
|
FALSE, // Inheritance - no
|
|
0, // Creation flags
|
|
NULL, // Environment
|
|
NULL, // Current dir
|
|
&si,
|
|
&procinfo))
|
|
{
|
|
TRACE((ERROR_MESSAGE,
|
|
"Error creating process (szCmdLine=%s), GetLastError=0x%x\n",
|
|
szCommandLine, GetLastError()));
|
|
CloseHandle(procinfo.hProcess);
|
|
CloseHandle(procinfo.hThread);
|
|
procinfo.hProcess = procinfo.hProcess = NULL;
|
|
|
|
rv = ERR_CREATING_PROCESS;
|
|
goto exiterr;
|
|
}
|
|
|
|
(*ppCI)->hProcess = procinfo.hProcess;
|
|
(*ppCI)->hThread = procinfo.hThread;
|
|
(*ppCI)->lProcessId = procinfo.dwProcessId;
|
|
(*ppCI)->dwThreadId = procinfo.dwThreadId;
|
|
|
|
rv = Wait4Connect(*ppCI);
|
|
if (rv || (*ppCI)->dead)
|
|
{
|
|
(*ppCI)->dead = TRUE;
|
|
TRACE((WARNING_MESSAGE, "Client can't connect\n"));
|
|
rv = ERR_CONNECTING;
|
|
goto exiterr;
|
|
}
|
|
|
|
hClient = (*ppCI)->hClient;
|
|
if ( NULL == hClient)
|
|
{
|
|
trys = 120; // 2 minutes
|
|
do {
|
|
hClient = _FindTopWindow(NAME_MAINCLASS,
|
|
NULL,
|
|
procinfo.dwProcessId);
|
|
if (!hClient)
|
|
{
|
|
Sleep(1000);
|
|
trys --;
|
|
}
|
|
} while(!hClient && trys);
|
|
|
|
if (!trys)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Can't connect"));
|
|
rv = ERR_CONNECTING;
|
|
goto exiterr;
|
|
}
|
|
}
|
|
|
|
// Find the clients child windows
|
|
trys = 240; // 2 min
|
|
do {
|
|
hContainer = _FindWindow(hClient, NULL, NAME_CONTAINERCLASS);
|
|
hInput = _FindWindow(hContainer, NULL, NAME_INPUT);
|
|
hOutput = _FindWindow(hContainer, NULL, NAME_OUTPUT);
|
|
if (!hContainer || !hInput || !hOutput)
|
|
{
|
|
TRACE((INFO_MESSAGE, "Can't get child windows. Retry"));
|
|
Sleep(500);
|
|
trys--;
|
|
}
|
|
} while ((!hContainer || !hInput || !hOutput) && trys);
|
|
|
|
if (!trys)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Can't find child windows"));
|
|
rv = ERR_CONNECTING;
|
|
goto exiterr;
|
|
}
|
|
|
|
TRACE((INFO_MESSAGE, "hClient = 0x%x\n", hClient));
|
|
TRACE((INFO_MESSAGE, "hContainer= 0x%x\n", hContainer));
|
|
TRACE((INFO_MESSAGE, "hInput = 0x%x\n", hInput));
|
|
TRACE((INFO_MESSAGE, "hOutput = 0x%x\n", hOutput));
|
|
|
|
|
|
(*ppCI)->hClient = hClient;
|
|
(*ppCI)->hContainer = hContainer;
|
|
(*ppCI)->hInput = hInput;
|
|
(*ppCI)->hOutput = hOutput;
|
|
} else {
|
|
// Else what !? This is RCLX mode
|
|
// Go in wait mode and wait until some client is connected
|
|
// remotely
|
|
// set flag in context that this connection works only with remote client
|
|
|
|
// find the valid server name
|
|
while (*lpszServerName && (*lpszServerName) == L'\\')
|
|
lpszServerName ++;
|
|
|
|
TRACE((INFO_MESSAGE,
|
|
"A thread in RCLX mode. Wait for some client."
|
|
"The target is: %S\n", lpszServerName));
|
|
|
|
(*ppCI)->dead = FALSE;
|
|
(*ppCI)->RClxMode = TRUE;
|
|
(*ppCI)->dwThreadId = GetCurrentThreadId();
|
|
_AddToClientQ(*ppCI);
|
|
|
|
rv = _Wait4ConnectTimeout(*ppCI, INFINITE);
|
|
if (rv || (*ppCI)->dead)
|
|
{
|
|
(*ppCI)->dead = TRUE;
|
|
TRACE((WARNING_MESSAGE, "Client can't connect to the test controler (us)\n"));
|
|
rv = ERR_CONNECTING;
|
|
goto exiterr;
|
|
} else {
|
|
// dwProcessId contains socket. hClient is pointer to RCLX
|
|
// context structure, aren't they ?
|
|
ASSERT((*ppCI)->lProcessId != INVALID_SOCKET);
|
|
ASSERT((*ppCI)->hClient);
|
|
|
|
TRACE((INFO_MESSAGE, "Client received remote connection\n"));
|
|
}
|
|
|
|
// Next, send connection info to the remote client
|
|
// like server to connect to, resolution, etc.
|
|
if (!RClx_SendConnectInfo(
|
|
(PRCLXCONTEXT)((*ppCI)->hClient),
|
|
lpszServerName,
|
|
xxRes,
|
|
yyRes,
|
|
ConnectionFlags))
|
|
{
|
|
(*ppCI)->dead = TRUE;
|
|
TRACE((WARNING_MESSAGE, "Client can't send connection info\n"));
|
|
rv = ERR_CONNECTING;
|
|
goto exiterr;
|
|
}
|
|
|
|
// Now again wait for connect event
|
|
// this time it will be real
|
|
rv = Wait4Connect(*ppCI);
|
|
if ((*ppCI)->bWillCallAgain)
|
|
{
|
|
// if so, now the client is disconnected
|
|
TRACE((INFO_MESSAGE, "Wait for second call\n"));
|
|
(*ppCI)->dead = FALSE;
|
|
|
|
rv = Wait4Connect(*ppCI);
|
|
// Wait for second connect
|
|
rv = Wait4Connect(*ppCI);
|
|
|
|
}
|
|
|
|
if (rv || (*ppCI)->dead)
|
|
{
|
|
(*ppCI)->dead = TRUE;
|
|
TRACE((WARNING_MESSAGE, "Client(mstsc) can't connect to TS\n"));
|
|
rv = ERR_CONNECTING;
|
|
goto exiterr;
|
|
}
|
|
}
|
|
|
|
// Save the resolution
|
|
(*ppCI)->xRes = xxRes;
|
|
(*ppCI)->yRes = yyRes;
|
|
|
|
// If username is present try to login
|
|
if (wcslen(lpszUserName))
|
|
{
|
|
rv = _Login(*ppCI, lpszUserName, lpszPassword, lpszDomain);
|
|
if (rv)
|
|
goto exiterr;
|
|
}
|
|
|
|
exitpt:
|
|
return rv;
|
|
exiterr:
|
|
if (*ppCI)
|
|
{
|
|
if ((szDiscon = SCDisconnect(*ppCI)))
|
|
{
|
|
TRACE(( WARNING_MESSAGE, "Error disconnecting: %s\n", szDiscon));
|
|
}
|
|
|
|
*ppCI = NULL;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
PROTOCOLAPI
|
|
LPCSTR
|
|
SMCAPI
|
|
SCConnect(
|
|
LPCWSTR lpszServerName,
|
|
LPCWSTR lpszUserName,
|
|
LPCWSTR lpszPassword,
|
|
LPCWSTR lpszDomain,
|
|
IN const int xRes,
|
|
IN const int yRes,
|
|
PCONNECTINFO *ppCI)
|
|
{
|
|
return SCConnectEx(
|
|
lpszServerName,
|
|
lpszUserName,
|
|
lpszPassword,
|
|
lpszDomain,
|
|
NULL, // Default shell (MS Explorer)
|
|
xRes,
|
|
yRes,
|
|
g_ConnectionFlags, // compression, bmp cache, full screen
|
|
ppCI);
|
|
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* SCDisconnect
|
|
* Description:
|
|
* Called by smclient, when disconnect command is interpreted
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* Return value:
|
|
* Error message. NULL on success
|
|
* Called by:
|
|
* !smclient
|
|
--*/
|
|
PROTOCOLAPI
|
|
LPCSTR
|
|
SMCAPI
|
|
SCDisconnect(
|
|
PCONNECTINFO pCI)
|
|
{
|
|
LPCSTR rv = NULL;
|
|
INT nCloseTime = WAIT4STR_TIMEOUT;
|
|
INT nCloseTries = 0;
|
|
DWORD wres;
|
|
HWND hYesNo = NULL;
|
|
HWND hDiscBox = NULL;
|
|
HWND hDialog = NULL;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!(pCI->RClxMode))
|
|
{
|
|
// Try to close the client window
|
|
if (!pCI->hClient)
|
|
pCI->hClient = _FindTopWindow(NAME_MAINCLASS,
|
|
NULL,
|
|
pCI->lProcessId);
|
|
|
|
if (pCI->hClient)
|
|
SendMessage(pCI->hClient, WM_CLOSE, 0, 0);
|
|
|
|
do {
|
|
// search for disconnect dialog and close it
|
|
if (!hDialog && !hDiscBox &&
|
|
(hDiscBox =
|
|
_FindTopWindow(NULL,
|
|
g_strDisconnectDialogBox,
|
|
pCI->lProcessId)))
|
|
PostMessage(hDiscBox, WM_CLOSE, 0, 0);
|
|
|
|
// If the client asks whether to close or not
|
|
// Answer with 'Yes'
|
|
|
|
if (!hYesNo)
|
|
hYesNo = _FindTopWindow(NULL,
|
|
g_strYesNoShutdown,
|
|
pCI->lProcessId);
|
|
|
|
if (hYesNo &&
|
|
(nCloseTries % 10) == 1)
|
|
PostMessage(hYesNo, WM_KEYDOWN, VK_RETURN, 0);
|
|
else if ((nCloseTries % 10) == 5)
|
|
{
|
|
// On every 10 attempts retry to close the client
|
|
if (!pCI->hClient)
|
|
pCI->hClient = _FindTopWindow(NAME_MAINCLASS,
|
|
NULL,
|
|
pCI->lProcessId);
|
|
|
|
if (pCI->hClient)
|
|
PostMessage(pCI->hClient, WM_CLOSE, 0, 0);
|
|
}
|
|
|
|
nCloseTries++;
|
|
nCloseTime -= 3000;
|
|
} while (
|
|
(wres = WaitForSingleObject(pCI->hProcess, 3000)) ==
|
|
WAIT_TIMEOUT &&
|
|
nCloseTime > 0
|
|
);
|
|
|
|
if (wres == WAIT_TIMEOUT)
|
|
{
|
|
TRACE((WARNING_MESSAGE,
|
|
"Can't close process. WaitForSingleObject timeouts\n"));
|
|
TRACE((WARNING_MESSAGE,
|
|
"Process #%d will be killed\n",
|
|
pCI->lProcessId ));
|
|
if (!TerminateProcess(pCI->hProcess, 1))
|
|
{
|
|
TRACE((WARNING_MESSAGE,
|
|
"Can't kill process #%p. GetLastError=%d\n",
|
|
pCI->lProcessId, GetLastError()));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (!_RemoveFromClientQ(pCI))
|
|
{
|
|
TRACE(( WARNING_MESSAGE,
|
|
"Couldn't find CONNECTINFO in the queue\n" ));
|
|
}
|
|
|
|
_CloseConnectInfo(pCI);
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* SCLogoff
|
|
* Description:
|
|
* Called by smclient, when logoff command is interpreted
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* Return value:
|
|
* Error message. NULL on success
|
|
* Called by:
|
|
* !smclient
|
|
--*/
|
|
PROTOCOLAPI
|
|
LPCSTR
|
|
SMCAPI
|
|
SCLogoff(
|
|
PCONNECTINFO pCI)
|
|
{
|
|
LPCSTR rv = NULL;
|
|
INT retries = 5;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (pCI->dead)
|
|
{
|
|
rv = ERR_CLIENT_IS_DEAD;
|
|
goto disconnectpt;
|
|
}
|
|
|
|
do {
|
|
// Send Ctrl+Esc
|
|
SCSenddata(pCI, WM_KEYDOWN, 17, 1900545);
|
|
SCSenddata(pCI, WM_KEYDOWN, 27, 65537);
|
|
SCSenddata(pCI, WM_KEYUP, 27, -1073676287);
|
|
SCSenddata(pCI, WM_KEYUP, 17, -1071841279);
|
|
// Wait for Run... menu
|
|
rv = _Wait4Str(pCI, g_strStartRun, WAIT4STR_TIMEOUT/4, WAIT_STRING);
|
|
|
|
if (rv)
|
|
goto next_retry;
|
|
|
|
// Send three times Key-Up (scan code 72) and <Enter>
|
|
SCSendtextAsMsgs(pCI, g_strStartLogoff);
|
|
|
|
rv = _Wait4Str(pCI,
|
|
g_strNTSecurity,
|
|
WAIT4STR_TIMEOUT/4,
|
|
WAIT_STRING);
|
|
next_retry:
|
|
retries --;
|
|
} while (rv && retries);
|
|
|
|
if (rv)
|
|
goto disconnectpt;
|
|
|
|
for (retries = 5; retries; retries--) {
|
|
SCSendtextAsMsgs(pCI, g_strNTSecurity_Act);
|
|
|
|
rv = Wait4Str(pCI, g_strSureLogoff);
|
|
|
|
if (!rv) break;
|
|
}
|
|
|
|
if (rv)
|
|
goto disconnectpt;
|
|
|
|
SCSendtextAsMsgs(pCI, g_strSureLogoffAct); // Press enter
|
|
|
|
rv = Wait4Disconnect(pCI);
|
|
if (rv)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Can't close the connection\n"));
|
|
}
|
|
|
|
disconnectpt:
|
|
rv = SCDisconnect(pCI);
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* SCStart
|
|
* Description:
|
|
* Called by smclient, when start command is interpreted
|
|
* This functions emulates starting an app from Start->Run menu
|
|
* on the server side
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* lpszAppName - command line
|
|
* Return value:
|
|
* Error message. NULL on success
|
|
* Called by:
|
|
* !smclient
|
|
--*/
|
|
PROTOCOLAPI
|
|
LPCSTR
|
|
SMCAPI
|
|
SCStart(
|
|
PCONNECTINFO pCI, LPCWSTR lpszAppName)
|
|
{
|
|
LPCSTR waitres = NULL;
|
|
// int retries = 5;
|
|
// int retries2 = 5;
|
|
DWORD dwTimeout;
|
|
LPCSTR rv = NULL;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (pCI->dead)
|
|
{
|
|
rv = ERR_CLIENT_IS_DEAD;
|
|
goto exitpt;
|
|
}
|
|
|
|
dwTimeout = 10000; // start the timeout of 10 secs
|
|
// Try to start run menu
|
|
do {
|
|
// Press Ctrl+Esc
|
|
do {
|
|
TRACE((ALIVE_MESSAGE, "Start: Sending Ctrl+Esc\n"));
|
|
SCSenddata(pCI, WM_KEYDOWN, 17, 1900545);
|
|
SCSenddata(pCI, WM_KEYDOWN, 27, 65537);
|
|
SCSenddata(pCI, WM_KEYUP, 27, -1073676287);
|
|
SCSenddata(pCI, WM_KEYUP, 17, -1071841279);
|
|
|
|
// If the last wait was unsuccessfull increase the timeout
|
|
if (waitres)
|
|
dwTimeout += 2000;
|
|
|
|
// Wait for Run... menu
|
|
waitres = _Wait4Str(pCI,
|
|
g_strStartRun,
|
|
dwTimeout,
|
|
WAIT_STRING);
|
|
|
|
if (waitres)
|
|
{
|
|
TRACE((INFO_MESSAGE, "Start: Start menu didn't appear. Retrying\n"));
|
|
} else {
|
|
TRACE((ALIVE_MESSAGE, "Start: Got the start menu\n"));
|
|
}
|
|
} while (waitres && dwTimeout < WAIT4STR_TIMEOUT);
|
|
|
|
if (waitres)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Start: Start menu didn't appear. Giving up\n"));
|
|
rv = ERR_START_MENU_NOT_APPEARED;
|
|
goto exitpt;
|
|
}
|
|
|
|
TRACE((ALIVE_MESSAGE, "Start: Waiting for the \"Run\" box\n"));
|
|
// press 'R' for Run...
|
|
SCSendtextAsMsgs(pCI, g_strStartRun_Act);
|
|
waitres = _Wait4Str(pCI,
|
|
g_strRunBox,
|
|
dwTimeout,
|
|
WAIT_STRING);
|
|
if (waitres)
|
|
// No success, press Esc
|
|
{
|
|
TRACE((INFO_MESSAGE, "Start: Can't get the \"Run\" box. Retrying\n"));
|
|
SCSenddata(pCI, WM_KEYDOWN, 27, 65537);
|
|
SCSenddata(pCI, WM_KEYUP, 27, -1073676287);
|
|
}
|
|
} while (waitres && dwTimeout < WAIT4STR_TIMEOUT);
|
|
|
|
if (waitres)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Start: \"Run\" box didn't appear. Giving up\n"));
|
|
rv = ERR_COULDNT_OPEN_PROGRAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
TRACE((ALIVE_MESSAGE, "Start: Sending the command line\n"));
|
|
// Now we have the focus on the "Run" box, send the app name
|
|
rv = SCSendtextAsMsgs(pCI, lpszAppName);
|
|
|
|
// Hit <Enter>
|
|
SCSenddata(pCI, WM_KEYDOWN, 13, 1835009);
|
|
SCSenddata(pCI, WM_KEYUP, 13, -1071906815);
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
|
|
// Eventualy, we are going to change the clipboard
|
|
// Syncronize this, so no other thread's AV while
|
|
// checking the clipboard content
|
|
// store 1 for write, 0 for read
|
|
static LONG g_ClipOpened = 0;
|
|
|
|
/*++
|
|
* Function:
|
|
* SCClipbaord
|
|
* Description:
|
|
* Called by smclient, when clipboard command is interpreted
|
|
* when eClipOp is COPY_TO_CLIPBOARD it copies the lpszFileName to
|
|
* the clipboard. If eClipOp is PASTE_FROM_CLIPBOARD it
|
|
* checks the clipboard content against the content of lpszFileName
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* eClipOp - clipboard operation. Possible values:
|
|
* COPY_TO_CLIPBOARD and PASTE_FROM_CLIPBOARD
|
|
* Return value:
|
|
* Error message. NULL on success
|
|
* Called by:
|
|
* !smclient
|
|
--*/
|
|
PROTOCOLAPI
|
|
LPCSTR
|
|
SMCAPI
|
|
SCClipboard(
|
|
PCONNECTINFO pCI, const CLIPBOARDOPS eClipOp, LPCSTR lpszFileName)
|
|
{
|
|
LPCSTR rv = NULL;
|
|
INT hFile = -1;
|
|
LONG clplength = 0;
|
|
UINT uiFormat = 0;
|
|
HGLOBAL ghClipData = NULL;
|
|
HGLOBAL hNewData = NULL;
|
|
PBYTE pClipData = NULL;
|
|
BOOL bClipboardOpen = FALSE;
|
|
BOOL bFreeClipHandle = TRUE;
|
|
|
|
LONG prevOp = 1;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (pCI->dead)
|
|
{
|
|
rv = ERR_CLIENT_IS_DEAD;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (lpszFileName == NULL || !(*lpszFileName))
|
|
{
|
|
// No filename specified, work like an empty clipboard is requested
|
|
if (eClipOp == COPY_TO_CLIPBOARD)
|
|
if (pCI->RClxMode)
|
|
{
|
|
if (!RClx_SendClipboard((PRCLXCONTEXT)(pCI->hClient),
|
|
NULL, 0, 0))
|
|
rv = ERR_COPY_CLIPBOARD;
|
|
} else {
|
|
if (!Clp_EmptyClipboard())
|
|
rv = ERR_COPY_CLIPBOARD;
|
|
}
|
|
else if (eClipOp == PASTE_FROM_CLIPBOARD)
|
|
{
|
|
if (pCI->RClxMode)
|
|
{
|
|
if (!RClx_SendClipboardRequest((PRCLXCONTEXT)(pCI->hClient), 0))
|
|
{
|
|
rv = ERR_PASTE_CLIPBOARD;
|
|
goto exitpt;
|
|
}
|
|
if (_Wait4ClipboardTimeout(pCI, WAIT4STR_TIMEOUT))
|
|
{
|
|
rv = ERR_PASTE_CLIPBOARD;
|
|
goto exitpt;
|
|
}
|
|
|
|
// We do not expect to receive clipboard data
|
|
// just format ID
|
|
if (!pCI->uiClipboardFormat)
|
|
// if the format is 0, then there's no clipboard
|
|
rv = NULL;
|
|
else
|
|
rv = ERR_PASTE_CLIPBOARD_DIFFERENT_SIZE;
|
|
} else {
|
|
if (Clp_CheckEmptyClipboard())
|
|
rv = NULL;
|
|
else
|
|
rv = ERR_PASTE_CLIPBOARD_DIFFERENT_SIZE;
|
|
}
|
|
} else {
|
|
TRACE((ERROR_MESSAGE, "SCClipboard: Invalid filename\n"));
|
|
rv = ERR_INVALID_PARAM;
|
|
}
|
|
goto exitpt;
|
|
}
|
|
|
|
if (eClipOp == COPY_TO_CLIPBOARD)
|
|
{
|
|
// Open the file for reading
|
|
hFile = _open(lpszFileName, _O_RDONLY|_O_BINARY);
|
|
if (hFile == -1)
|
|
{
|
|
TRACE((ERROR_MESSAGE,
|
|
"Error opening file: %s. errno=%d\n", lpszFileName, errno));
|
|
rv = ERR_COPY_CLIPBOARD;
|
|
goto exitpt;
|
|
}
|
|
|
|
// Get the clipboard length (in the file)
|
|
clplength = _filelength(hFile) - sizeof(uiFormat);
|
|
// Get the format
|
|
if (_read(hFile, &uiFormat, sizeof(uiFormat)) != sizeof(uiFormat))
|
|
{
|
|
TRACE((ERROR_MESSAGE,
|
|
"Error reading from file. errno=%d\n", errno));
|
|
rv = ERR_COPY_CLIPBOARD;
|
|
goto exitpt;
|
|
}
|
|
|
|
ghClipData = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, clplength);
|
|
if (!ghClipData)
|
|
{
|
|
TRACE((ERROR_MESSAGE,
|
|
"Can't allocate %d bytes\n", clplength));
|
|
rv = ERR_COPY_CLIPBOARD;
|
|
goto exitpt;
|
|
}
|
|
|
|
pClipData = GlobalLock(ghClipData);
|
|
if (!pClipData)
|
|
{
|
|
TRACE((ERROR_MESSAGE,
|
|
"Can't lock handle 0x%x\n", ghClipData));
|
|
rv = ERR_COPY_CLIPBOARD;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (_read(hFile, pClipData, clplength) != clplength)
|
|
{
|
|
TRACE((ERROR_MESSAGE,
|
|
"Error reading from file. errno=%d\n", errno));
|
|
rv = ERR_COPY_CLIPBOARD;
|
|
goto exitpt;
|
|
}
|
|
|
|
GlobalUnlock(ghClipData);
|
|
|
|
if (pCI->RClxMode)
|
|
// RCLX mode, send the data to the client's machine
|
|
{
|
|
if (!(pClipData = GlobalLock(ghClipData)))
|
|
{
|
|
rv = ERR_COPY_CLIPBOARD;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!RClx_SendClipboard((PRCLXCONTEXT)(pCI->hClient),
|
|
pClipData, clplength, uiFormat))
|
|
{
|
|
rv = ERR_COPY_CLIPBOARD;
|
|
goto exitpt;
|
|
}
|
|
} else {
|
|
// Local mode, change the clipboard on this machine
|
|
if ((prevOp = InterlockedExchange(&g_ClipOpened, 1)))
|
|
{
|
|
rv = ERR_CLIPBOARD_LOCKED;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!OpenClipboard(NULL))
|
|
{
|
|
TRACE((ERROR_MESSAGE,
|
|
"Can't open the clipboard. GetLastError=%d\n",
|
|
GetLastError()));
|
|
rv = ERR_COPY_CLIPBOARD;
|
|
goto exitpt;
|
|
}
|
|
|
|
bClipboardOpen = TRUE;
|
|
|
|
// Empty the clipboard, so we'll have only one entry
|
|
EmptyClipboard();
|
|
|
|
if (!Clp_SetClipboardData(uiFormat, ghClipData, clplength, &bFreeClipHandle))
|
|
{
|
|
TRACE((ERROR_MESSAGE,
|
|
"SetClipboardData failed. GetLastError=%d\n",
|
|
GetLastError()));
|
|
rv = ERR_COPY_CLIPBOARD;
|
|
goto exitpt;
|
|
}
|
|
}
|
|
|
|
} else if (eClipOp == PASTE_FROM_CLIPBOARD)
|
|
{
|
|
LONG nClipDataSize;
|
|
|
|
// Open the file for reading
|
|
hFile = _open(lpszFileName, _O_RDONLY|_O_BINARY);
|
|
if (hFile == -1)
|
|
{
|
|
TRACE((ERROR_MESSAGE,
|
|
"Error opening file: %s. errno=%d\n", lpszFileName, errno));
|
|
rv = ERR_PASTE_CLIPBOARD;
|
|
goto exitpt;
|
|
}
|
|
|
|
// Get the clipboard length (in the file)
|
|
clplength = _filelength(hFile) - sizeof(uiFormat);
|
|
// Get the format
|
|
if (_read(hFile, &uiFormat, sizeof(uiFormat)) != sizeof(uiFormat))
|
|
{
|
|
TRACE((ERROR_MESSAGE,
|
|
"Error reading from file. errno=%d\n", errno));
|
|
rv = ERR_PASTE_CLIPBOARD;
|
|
goto exitpt;
|
|
}
|
|
|
|
// This piece retrieves the clipboard
|
|
if (pCI->RClxMode)
|
|
// Send request for a clipboard
|
|
{
|
|
if (!RClx_SendClipboardRequest((PRCLXCONTEXT)(pCI->hClient), uiFormat))
|
|
{
|
|
rv = ERR_PASTE_CLIPBOARD;
|
|
goto exitpt;
|
|
}
|
|
if (_Wait4ClipboardTimeout(pCI, WAIT4STR_TIMEOUT))
|
|
{
|
|
rv = ERR_PASTE_CLIPBOARD;
|
|
goto exitpt;
|
|
}
|
|
|
|
ghClipData = pCI->ghClipboard;
|
|
// Get the clipboard size
|
|
nClipDataSize = pCI->nClipboardSize;
|
|
} else {
|
|
// retrieve the local clipboard
|
|
if ((prevOp = InterlockedExchange(&g_ClipOpened, 1)))
|
|
{
|
|
rv = ERR_CLIPBOARD_LOCKED;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!OpenClipboard(NULL))
|
|
{
|
|
TRACE((ERROR_MESSAGE,
|
|
"Can't open the clipboard. GetLastError=%d\n",
|
|
GetLastError()));
|
|
rv = ERR_PASTE_CLIPBOARD;
|
|
goto exitpt;
|
|
}
|
|
|
|
bClipboardOpen = TRUE;
|
|
|
|
// Retrieve the data
|
|
ghClipData = GetClipboardData(uiFormat);
|
|
if (ghClipData)
|
|
{
|
|
Clp_GetClipboardData(uiFormat,
|
|
ghClipData,
|
|
&nClipDataSize,
|
|
&hNewData);
|
|
bFreeClipHandle = FALSE;
|
|
}
|
|
|
|
if (hNewData)
|
|
ghClipData = hNewData;
|
|
}
|
|
|
|
if (!ghClipData)
|
|
{
|
|
TRACE((ERROR_MESSAGE,
|
|
"Can't get clipboard data (empty clipboard ?). GetLastError=%d\n",
|
|
GetLastError()));
|
|
rv = ERR_PASTE_CLIPBOARD_EMPTY;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!nClipDataSize)
|
|
TRACE((WARNING_MESSAGE, "Clipboard is empty.\n"));
|
|
|
|
pClipData = GlobalLock(ghClipData);
|
|
if (!pClipData)
|
|
{
|
|
TRACE((ERROR_MESSAGE,
|
|
"Can't lock global mem. GetLastError=%d\n",
|
|
GetLastError()));
|
|
rv = ERR_PASTE_CLIPBOARD;
|
|
goto exitpt;
|
|
}
|
|
|
|
// Check if the client is on Win16 platform
|
|
// and the clipboard is paragraph aligned
|
|
// the file size is just bellow this size
|
|
if (pCI->RClxMode &&
|
|
(strstr(pCI->szClientType, "WIN16") != NULL) &&
|
|
((nClipDataSize % 16) == 0) &&
|
|
((nClipDataSize - clplength) < 16) &&
|
|
(nClipDataSize != 0))
|
|
{
|
|
// if so, then cut the clipboard size with the difference
|
|
nClipDataSize = clplength;
|
|
}
|
|
else if (nClipDataSize != clplength)
|
|
{
|
|
TRACE((INFO_MESSAGE, "Different length: file=%d, clipbrd=%d\n",
|
|
clplength, nClipDataSize));
|
|
rv = ERR_PASTE_CLIPBOARD_DIFFERENT_SIZE;
|
|
goto exitpt;
|
|
}
|
|
|
|
// compare the data
|
|
{
|
|
BYTE pBuff[1024];
|
|
PBYTE pClp = pClipData;
|
|
UINT nBytes;
|
|
BOOL bEqu = TRUE;
|
|
|
|
while (bEqu &&
|
|
(nBytes = _read(hFile, pBuff, sizeof(pBuff))) &&
|
|
nBytes != -1)
|
|
{
|
|
if (memcmp(pBuff, pClp, nBytes))
|
|
bEqu = FALSE;
|
|
|
|
pClp += nBytes;
|
|
}
|
|
|
|
if (!bEqu)
|
|
{
|
|
TRACE((INFO_MESSAGE, "Clipboard and file are not equal\n"));
|
|
rv = ERR_PASTE_CLIPBOARD_NOT_EQUAL;
|
|
}
|
|
}
|
|
|
|
} else
|
|
rv = ERR_UNKNOWN_CLIPBOARD_OP;
|
|
|
|
exitpt:
|
|
// Do the cleanup
|
|
|
|
// Release the clipboard handle
|
|
if (pClipData)
|
|
GlobalUnlock(ghClipData);
|
|
|
|
// free any clipboard received in RCLX mode
|
|
if (pCI->RClxMode && pCI->ghClipboard)
|
|
{
|
|
GlobalFree(pCI->ghClipboard);
|
|
pCI->ghClipboard = NULL;
|
|
}
|
|
else if (ghClipData && eClipOp == COPY_TO_CLIPBOARD && bFreeClipHandle)
|
|
GlobalFree(ghClipData);
|
|
|
|
if (hNewData)
|
|
GlobalFree(hNewData);
|
|
|
|
// Close the file
|
|
if (hFile != -1)
|
|
_close(hFile);
|
|
|
|
// Close the clipboard
|
|
if (bClipboardOpen)
|
|
CloseClipboard();
|
|
if (!prevOp)
|
|
InterlockedExchange(&g_ClipOpened, 0);
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* SCSaveClipboard
|
|
* Description:
|
|
* Save the clipboard in file (szFileName) with
|
|
* format specified in szFormatName
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* szFormatName- format name
|
|
* szFileName - the name of the file to save to
|
|
* Return value:
|
|
* Error message. NULL on success
|
|
* Called by:
|
|
* !perlext
|
|
--*/
|
|
PROTOCOLAPI
|
|
LPCSTR
|
|
SMCAPI
|
|
SCSaveClipboard(
|
|
PCONNECTINFO pCI,
|
|
LPCSTR szFormatName,
|
|
LPCSTR szFileName)
|
|
{
|
|
LPCSTR rv = ERR_SAVE_CLIPBOARD;
|
|
BOOL bClipboardOpen = FALSE;
|
|
UINT nFormatID = 0;
|
|
HGLOBAL ghClipData = NULL;
|
|
HGLOBAL hNewData = NULL;
|
|
INT nClipDataSize;
|
|
CHAR *pClipData = NULL;
|
|
INT hFile = -1;
|
|
|
|
LONG prevOp = 1;
|
|
|
|
// ++++++ First go thru parameter check
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (pCI->dead)
|
|
{
|
|
rv = ERR_CLIENT_IS_DEAD;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (szFormatName == NULL || !(*szFormatName))
|
|
{
|
|
TRACE((ERROR_MESSAGE, "SCClipboard: Invalid format name\n"));
|
|
rv = ERR_INVALID_PARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (szFileName == NULL || !(*szFileName))
|
|
{
|
|
TRACE((ERROR_MESSAGE, "SCClipboard: Invalid filename\n"));
|
|
rv = ERR_INVALID_PARAM;
|
|
goto exitpt;
|
|
}
|
|
// ------ End of parameter check
|
|
//
|
|
|
|
if (pCI->RClxMode)
|
|
{
|
|
nFormatID = _GetKnownClipboardFormatIDByName(szFormatName);
|
|
if (!nFormatID)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "Can't get the clipboard format ID: %s.\n", szFormatName));
|
|
goto exitpt;
|
|
}
|
|
|
|
// Send request for a clipboard
|
|
if (!RClx_SendClipboardRequest((PRCLXCONTEXT)(pCI->hClient), nFormatID))
|
|
{
|
|
rv = ERR_PASTE_CLIPBOARD;
|
|
goto exitpt;
|
|
}
|
|
if (_Wait4ClipboardTimeout(pCI, WAIT4STR_TIMEOUT))
|
|
{
|
|
rv = ERR_PASTE_CLIPBOARD;
|
|
goto exitpt;
|
|
}
|
|
|
|
ghClipData = pCI->ghClipboard;
|
|
// Get the clipboard size
|
|
nClipDataSize = pCI->nClipboardSize;
|
|
|
|
if (!ghClipData || !nClipDataSize)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Clipboard is empty.\n"));
|
|
goto exitpt;
|
|
}
|
|
} else {
|
|
// local mode
|
|
// Open the clipboard
|
|
|
|
if ((prevOp = InterlockedExchange(&g_ClipOpened, 1)))
|
|
{
|
|
rv = ERR_CLIPBOARD_LOCKED;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!OpenClipboard(NULL))
|
|
{
|
|
TRACE((ERROR_MESSAGE, "Can't open the clipboard. GetLastError=%d\n",
|
|
GetLastError()));
|
|
goto exitpt;
|
|
}
|
|
|
|
bClipboardOpen = TRUE;
|
|
|
|
nFormatID = Clp_GetClipboardFormat(szFormatName);
|
|
|
|
if (!nFormatID)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "Can't get the clipboard format: %s.\n", szFormatName));
|
|
goto exitpt;
|
|
}
|
|
|
|
TRACE((INFO_MESSAGE, "Format ID: %d(0x%X)\n", nFormatID, nFormatID));
|
|
|
|
// Retrieve the data
|
|
ghClipData = GetClipboardData(nFormatID);
|
|
if (!ghClipData)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "Can't get clipboard data. GetLastError=%d\n", GetLastError()));
|
|
goto exitpt;
|
|
}
|
|
|
|
Clp_GetClipboardData(nFormatID, ghClipData, &nClipDataSize, &hNewData);
|
|
if (hNewData)
|
|
ghClipData = hNewData;
|
|
|
|
if (!nClipDataSize)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Clipboard is empty.\n"));
|
|
goto exitpt;
|
|
}
|
|
}
|
|
|
|
pClipData = GlobalLock(ghClipData);
|
|
if (!pClipData)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "Can't lock global mem. GetLastError=%d\n", GetLastError()));
|
|
goto exitpt;
|
|
}
|
|
|
|
// Open the destination file
|
|
hFile = _open(szFileName,
|
|
_O_RDWR|_O_CREAT|_O_BINARY|_O_TRUNC,
|
|
_S_IREAD|_S_IWRITE);
|
|
if (hFile == -1)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "Can't open a file: %s\n", szFileName));
|
|
goto exitpt;
|
|
}
|
|
|
|
// First write the format type
|
|
if (_write(hFile, &nFormatID, sizeof(nFormatID)) != sizeof(nFormatID))
|
|
{
|
|
TRACE((ERROR_MESSAGE, "_write failed. errno=%d\n", errno));
|
|
goto exitpt;
|
|
}
|
|
|
|
if (_write(hFile, pClipData, nClipDataSize) != (INT)nClipDataSize)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "_write failed. errno=%d\n", errno));
|
|
goto exitpt;
|
|
}
|
|
|
|
TRACE((INFO_MESSAGE, "File written successfully. %d bytes written\n", nClipDataSize));
|
|
|
|
rv = NULL;
|
|
exitpt:
|
|
// Do the cleanup
|
|
|
|
// Close the file
|
|
if (hFile != -1)
|
|
_close(hFile);
|
|
|
|
// Release the clipboard handle
|
|
if (pClipData)
|
|
GlobalUnlock(ghClipData);
|
|
|
|
if (hNewData)
|
|
GlobalFree(hNewData);
|
|
|
|
// Close the clipboard
|
|
if (bClipboardOpen)
|
|
CloseClipboard();
|
|
if (!prevOp)
|
|
InterlockedExchange(&g_ClipOpened, 0);
|
|
|
|
// free any clipboard received in RCLX mode
|
|
if (pCI && pCI->RClxMode && pCI->ghClipboard)
|
|
{
|
|
GlobalFree(pCI->ghClipboard);
|
|
pCI->ghClipboard = NULL;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* SCSenddata
|
|
* Description:
|
|
* Called by smclient, when senddata command is interpreted
|
|
* Sends an window message to the client
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* uiMessage - the massage Id
|
|
* wParam - word param of the message
|
|
* lParam - long param of the message
|
|
* Return value:
|
|
* Error message. NULL on success
|
|
* Called by:
|
|
* !smclient
|
|
--*/
|
|
PROTOCOLAPI
|
|
LPCSTR
|
|
SMCAPI
|
|
SCSenddata(
|
|
PCONNECTINFO pCI,
|
|
const UINT uiMessage,
|
|
const WPARAM wParam,
|
|
const LPARAM lParam)
|
|
{
|
|
UINT msg = uiMessage;
|
|
LPCSTR rv = NULL;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (pCI->dead)
|
|
{
|
|
rv = ERR_CLIENT_IS_DEAD;
|
|
goto exitpt;
|
|
}
|
|
|
|
// TRACE((ALIVE_MESSAGE, "Senddata: uMsg=%x wParam=%x lParam=%x\n",
|
|
// uiMessage, wParam, lParam));
|
|
|
|
// Determines whether it will
|
|
// send the message to local window
|
|
// or thru RCLX
|
|
if (!pCI->RClxMode)
|
|
{
|
|
// Obsolete, a client registry setting "Allow Background Input" asserts
|
|
// that the client will accept the message
|
|
// SetFocus(pCI->hInput);
|
|
// SendMessage(pCI->hInput, WM_SETFOCUS, 0, 0);
|
|
|
|
SendMessage(pCI->hInput, msg, wParam, lParam);
|
|
} else {
|
|
// RClxMode
|
|
ASSERT(pCI->lProcessId != INVALID_SOCKET);
|
|
ASSERT(pCI->hClient);
|
|
|
|
if (!RClx_SendMessage((PRCLXCONTEXT)(pCI->hClient),
|
|
msg, wParam, lParam))
|
|
{
|
|
TRACE((WARNING_MESSAGE,
|
|
"Can't send message thru RCLX\n"));
|
|
}
|
|
}
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
PROTOCOLAPI
|
|
LPCSTR
|
|
SMCAPI
|
|
SCClientTerminate(PCONNECTINFO pCI)
|
|
{
|
|
LPCSTR rv = ERR_CLIENTTERMINATE_FAIL;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!(pCI->RClxMode))
|
|
{
|
|
if (!TerminateProcess(pCI->hProcess, 1))
|
|
{
|
|
TRACE((WARNING_MESSAGE,
|
|
"Can't kill process #%p. GetLastError=%d\n",
|
|
pCI->lProcessId, GetLastError()));
|
|
goto exitpt;
|
|
}
|
|
} else {
|
|
TRACE((WARNING_MESSAGE,
|
|
"ClientTerminate is not supported in RCLX mode yet\n"));
|
|
TRACE((WARNING_MESSAGE, "Using disconnect\n"));
|
|
}
|
|
|
|
rv = SCDisconnect(pCI);
|
|
|
|
exitpt:
|
|
return rv;
|
|
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* SCGetSessionId
|
|
* Description:
|
|
* Called by smclient, returns the session ID. 0 is invalid, not logged on
|
|
* yet
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* Return value:
|
|
* session id, 0 is invlid value, -1 is returned on NT4 clients
|
|
* Called by:
|
|
* !smclient
|
|
--*/
|
|
PROTOCOLAPI
|
|
UINT
|
|
SMCAPI
|
|
SCGetSessionId(PCONNECTINFO pCI)
|
|
{
|
|
UINT rv = 0;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
goto exitpt;
|
|
}
|
|
|
|
if (pCI->dead)
|
|
{
|
|
goto exitpt;
|
|
}
|
|
|
|
rv = pCI->uiSessionId;
|
|
|
|
exitpt:
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* SCCheck
|
|
* Description:
|
|
* Called by smclient, when check command is interpreted
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* lpszCommand - command name
|
|
* lpszParam - command parameter
|
|
* Return value:
|
|
* Error message. NULL on success. Exceptions are GetDisconnectReason and
|
|
* GetWait4MultipleStrResult
|
|
* Called by:
|
|
* !smclient
|
|
--*/
|
|
PROTOCOLAPI
|
|
LPCSTR
|
|
SMCAPI
|
|
SCCheck(PCONNECTINFO pCI, LPCSTR lpszCommand, LPCWSTR lpszParam)
|
|
{
|
|
LPCSTR rv = ERR_INVALID_COMMAND;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (pCI->dead)
|
|
{
|
|
rv = ERR_CLIENT_IS_DEAD;
|
|
goto exitpt;
|
|
}
|
|
|
|
if ( !_stricmp(lpszCommand, "Wait4Str"))
|
|
rv = Wait4Str(pCI, lpszParam);
|
|
else if (!_stricmp(lpszCommand, "Wait4Disconnect"))
|
|
rv = Wait4Disconnect(pCI);
|
|
else if (!_stricmp(lpszCommand, "RegisterChat"))
|
|
rv = RegisterChat(pCI, lpszParam);
|
|
else if (!_stricmp(lpszCommand, "UnregisterChat"))
|
|
rv = UnregisterChat(pCI, lpszParam);
|
|
else if (!_stricmp(lpszCommand, "GetDisconnectReason"))
|
|
rv = GetDisconnectReason(pCI);
|
|
else if (!_stricmp(lpszCommand, "Wait4StrTimeout"))
|
|
rv = Wait4StrTimeout(pCI, lpszParam);
|
|
else if (!_stricmp(lpszCommand, "Wait4MultipleStr"))
|
|
rv = Wait4MultipleStr(pCI, lpszParam);
|
|
else if (!_stricmp(lpszCommand, "Wait4MultipleStrTimeout"))
|
|
rv = Wait4MultipleStrTimeout(pCI, lpszParam);
|
|
else if (!_stricmp(lpszCommand, "GetWait4MultipleStrResult"))
|
|
rv = GetWait4MultipleStrResult(pCI, lpszParam);
|
|
else if (!_stricmp(lpszCommand, "SwitchToProcess"))
|
|
rv = SCSwitchToProcess(pCI, lpszParam);
|
|
/* **New** */
|
|
else if (!_stricmp(lpszCommand, "SetClientTopmost"))
|
|
rv = SCSetClientTopmost(pCI, lpszParam);
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Extensions and help functions
|
|
*/
|
|
|
|
/*++
|
|
* Function:
|
|
* Wait4Disconnect
|
|
* Description:
|
|
* Waits until the client is disconnected
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* Return value:
|
|
* Error message. NULL on success
|
|
* Called by:
|
|
* SCCheck, SCLogoff
|
|
--*/
|
|
LPCSTR Wait4Disconnect(PCONNECTINFO pCI)
|
|
{
|
|
WAIT4STRING Wait;
|
|
LPCSTR rv = NULL;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (pCI->dead)
|
|
{
|
|
rv = ERR_CLIENT_IS_DEAD;
|
|
goto exitpt;
|
|
}
|
|
|
|
memset(&Wait, 0, sizeof(Wait));
|
|
Wait.evWait = CreateEvent(NULL, //security
|
|
TRUE, //manual
|
|
FALSE, //initial state
|
|
NULL); //name
|
|
|
|
Wait.lProcessId = pCI->lProcessId;
|
|
Wait.pOwner = pCI;
|
|
Wait.WaitType = WAIT_DISC;
|
|
|
|
rv = _WaitSomething(pCI, &Wait, WAIT4STR_TIMEOUT);
|
|
if (!rv)
|
|
{
|
|
TRACE(( INFO_MESSAGE, "Client is disconnected\n"));
|
|
}
|
|
|
|
CloseHandle(Wait.evWait);
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* Wait4Connect
|
|
* Description:
|
|
* Waits until the client is connect
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* Return value:
|
|
* Error message, NULL on success
|
|
* Called by:
|
|
* SCCOnnect
|
|
--*/
|
|
LPCSTR Wait4Connect(PCONNECTINFO pCI)
|
|
{
|
|
return (_Wait4ConnectTimeout(pCI, CONNECT_TIMEOUT));
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* _Wait4ConnectTimeout
|
|
* Description:
|
|
* Waits until the client is connect
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* dwTimeout - timeout value
|
|
* Return value:
|
|
* Error message, NULL on success
|
|
* Called by:
|
|
* SCConnect
|
|
--*/
|
|
LPCSTR _Wait4ConnectTimeout(PCONNECTINFO pCI, DWORD dwTimeout)
|
|
{
|
|
WAIT4STRING Wait;
|
|
LPCSTR rv = NULL;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
memset(&Wait, 0, sizeof(Wait));
|
|
Wait.evWait = CreateEvent(NULL, //security
|
|
TRUE, //manual
|
|
FALSE, //initial state
|
|
NULL); //name
|
|
|
|
Wait.lProcessId = pCI->lProcessId;
|
|
Wait.pOwner = pCI;
|
|
Wait.WaitType = WAIT_CONN;
|
|
|
|
rv = _WaitSomething(pCI, &Wait, dwTimeout);
|
|
if (!rv)
|
|
{
|
|
TRACE(( INFO_MESSAGE, "Client is connected\n"));
|
|
}
|
|
|
|
CloseHandle(Wait.evWait);
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* _Wait4ClipboardTimeout
|
|
* Description:
|
|
* Waits until clipboard response is received from RCLX module
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* dwTimeout - timeout value
|
|
* Return value:
|
|
* Error message, NULL on success
|
|
* Called by:
|
|
* SCClipboard
|
|
--*/
|
|
LPCSTR _Wait4ClipboardTimeout(PCONNECTINFO pCI, DWORD dwTimeout)
|
|
{
|
|
WAIT4STRING Wait;
|
|
LPCSTR rv = NULL;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!(pCI->RClxMode))
|
|
{
|
|
TRACE((WARNING_MESSAGE, "WaitForClipboard: Not in RCLX mode\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
memset(&Wait, 0, sizeof(Wait));
|
|
Wait.evWait = CreateEvent(NULL, //security
|
|
TRUE, //manual
|
|
FALSE, //initial state
|
|
NULL); //name
|
|
|
|
Wait.lProcessId = pCI->lProcessId;
|
|
Wait.pOwner = pCI;
|
|
Wait.WaitType = WAIT_CLIPBOARD;
|
|
|
|
rv = _WaitSomething(pCI, &Wait, dwTimeout);
|
|
if (!rv)
|
|
{
|
|
TRACE(( INFO_MESSAGE, "Clipboard received\n"));
|
|
}
|
|
|
|
CloseHandle(Wait.evWait);
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* GetDisconnectReason
|
|
* Description:
|
|
* Retrieves, if possible, the client error box
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* Return value:
|
|
* The error box message. NULL if not available
|
|
* Called by:
|
|
* SCCheck
|
|
--*/
|
|
LPCSTR GetDisconnectReason(PCONNECTINFO pCI)
|
|
{
|
|
HWND hDiscBox;
|
|
LPCSTR rv = NULL;
|
|
HWND hWnd, hwndTop, hwndNext;
|
|
char classname[128];
|
|
char caption[256];
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!pCI->dead)
|
|
{
|
|
rv = ERR_CLIENT_IS_DEAD;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (strlen(pCI->szDiscReason))
|
|
{
|
|
rv = pCI->szDiscReason;
|
|
goto exitpt;
|
|
}
|
|
|
|
hDiscBox = _FindTopWindow(NULL, DISCONNECT_DIALOG_BOX, pCI->lProcessId);
|
|
|
|
if (!hDiscBox)
|
|
{
|
|
rv = ERR_NORMAL_EXIT;
|
|
goto exitpt;
|
|
} else {
|
|
TRACE((INFO_MESSAGE, "Found hDiscBox=0x%x", hDiscBox));
|
|
}
|
|
|
|
pCI->szDiscReason[0] = 0;
|
|
hWnd = NULL;
|
|
|
|
hwndTop = GetWindow(hDiscBox, GW_CHILD);
|
|
if (!hwndTop)
|
|
{
|
|
TRACE((INFO_MESSAGE, "GetWindow failed. hwnd=0x%x\n", hDiscBox));
|
|
goto exitpt;
|
|
}
|
|
|
|
hwndNext = hwndTop;
|
|
do {
|
|
hWnd = hwndNext;
|
|
if (!GetClassName(hWnd, classname, sizeof(classname)))
|
|
{
|
|
TRACE((INFO_MESSAGE, "GetClassName failed. hwnd=0x%x\n", hWnd));
|
|
goto nextwindow;
|
|
}
|
|
if (!GetWindowText(hWnd, caption, sizeof(caption)))
|
|
{
|
|
TRACE((INFO_MESSAGE, "GetWindowText failed. hwnd=0x%x\n"));
|
|
goto nextwindow;
|
|
}
|
|
|
|
if (!strcmp(classname, STATIC_CLASS) &&
|
|
strlen(classname) <
|
|
sizeof(pCI->szDiscReason) - strlen(pCI->szDiscReason) - 3)
|
|
{
|
|
strcat(pCI->szDiscReason, caption);
|
|
strcat(pCI->szDiscReason, "\n");
|
|
}
|
|
nextwindow:
|
|
hwndNext = GetNextWindow(hWnd, GW_HWNDNEXT);
|
|
} while (hWnd && hwndNext != hwndTop);
|
|
|
|
rv = (LPCSTR)pCI->szDiscReason;
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* Wait4Str
|
|
* Description:
|
|
* Waits for a specific string to come from clients feedback
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* lpszParam - waited string
|
|
* Return value:
|
|
* Error message, NULL on success
|
|
* Called by:
|
|
* SCCheck
|
|
--*/
|
|
LPCSTR Wait4Str(PCONNECTINFO pCI, LPCWSTR lpszParam)
|
|
{
|
|
return _Wait4Str(pCI, lpszParam, WAIT4STR_TIMEOUT, WAIT_STRING);
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* Wait4StrTimeout
|
|
* Description:
|
|
* Waits for a specific string to come from clients feedback
|
|
* The timeout is different than default and is specified in
|
|
* lpszParam argument, like:
|
|
* waited_string<->timeout_value
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* lpszParam - waited string and timeout
|
|
* Return value:
|
|
* Error message, NULL on success
|
|
* Called by:
|
|
* SCCheck
|
|
--*/
|
|
LPCSTR Wait4StrTimeout(PCONNECTINFO pCI, LPCWSTR lpszParam)
|
|
{
|
|
WCHAR waitstr[MAX_STRING_LENGTH];
|
|
WCHAR *sep = wcsstr(lpszParam, CHAT_SEPARATOR);
|
|
DWORD dwTimeout;
|
|
LPCSTR rv = NULL;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (pCI->dead)
|
|
{
|
|
rv = ERR_CLIENT_IS_DEAD;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!sep)
|
|
{
|
|
TRACE((WARNING_MESSAGE,
|
|
"Wait4StrTiemout: No timeout value. Default applying\n"));
|
|
rv = Wait4Str(pCI, lpszParam);
|
|
} else {
|
|
LONG_PTR len = sep - lpszParam;
|
|
|
|
if (len > sizeof(waitstr) - 1)
|
|
len = sizeof(waitstr) - 1;
|
|
|
|
wcsncpy(waitstr, lpszParam, len);
|
|
waitstr[len] = 0;
|
|
sep += wcslen(CHAT_SEPARATOR);
|
|
dwTimeout = _wtoi(sep);
|
|
|
|
if (!dwTimeout)
|
|
{
|
|
TRACE((WARNING_MESSAGE,
|
|
"Wait4StrTiemout: No timeout value(%s). Default applying\n",
|
|
sep));
|
|
dwTimeout = WAIT4STR_TIMEOUT;
|
|
}
|
|
|
|
rv = _Wait4Str(pCI, waitstr, dwTimeout, WAIT_STRING);
|
|
}
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* Wait4MultipleStr
|
|
* Description:
|
|
* Same as Wait4Str, but waits for several strings at once
|
|
* the strings are separated by '|' character
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* lpszParam - waited strings
|
|
* Return value:
|
|
* Error message, NULL on success
|
|
* Called by:
|
|
* SCCheck
|
|
--*/
|
|
LPCSTR Wait4MultipleStr(PCONNECTINFO pCI, LPCWSTR lpszParam)
|
|
{
|
|
return _Wait4Str(pCI, lpszParam, WAIT4STR_TIMEOUT, WAIT_MSTRINGS);
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* Wait4MultipleStrTimeout
|
|
* Description:
|
|
* Combination between Wait4StrTimeout and Wait4MultipleStr
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* lpszParam - waited strings and timeout value. Example
|
|
* - "string1|string2|...|stringN<->5000"
|
|
* Return value:
|
|
* Error message, NULL on success
|
|
* Called by:
|
|
* SCCheck
|
|
--*/
|
|
LPCSTR Wait4MultipleStrTimeout(PCONNECTINFO pCI, LPCWSTR lpszParam)
|
|
{
|
|
WCHAR waitstr[MAX_STRING_LENGTH];
|
|
WCHAR *sep = wcsstr(lpszParam, CHAT_SEPARATOR);
|
|
DWORD dwTimeout;
|
|
LPCSTR rv = NULL;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (pCI->dead)
|
|
{
|
|
rv = ERR_CLIENT_IS_DEAD;
|
|
goto exitpt;
|
|
}
|
|
|
|
pCI->nWait4MultipleStrResult = 0;
|
|
pCI->szWait4MultipleStrResult[0] = 0;
|
|
|
|
if (!sep)
|
|
{
|
|
TRACE((WARNING_MESSAGE,
|
|
"Wait4MultipleStrTiemout: No timeout value. Default applying"));
|
|
rv = Wait4MultipleStr(pCI, lpszParam);
|
|
} else {
|
|
LONG_PTR len = sep - lpszParam;
|
|
|
|
if (len > sizeof(waitstr) - 1)
|
|
len = sizeof(waitstr) - 1;
|
|
|
|
wcsncpy(waitstr, lpszParam, len);
|
|
waitstr[len] = 0;
|
|
sep += wcslen(CHAT_SEPARATOR);
|
|
dwTimeout = _wtoi(sep);
|
|
|
|
if (!dwTimeout)
|
|
{
|
|
TRACE((WARNING_MESSAGE,
|
|
"Wait4StrTiemout: No timeout value. Default applying"));
|
|
dwTimeout = WAIT4STR_TIMEOUT;
|
|
}
|
|
|
|
rv = _Wait4Str(pCI, waitstr, dwTimeout, WAIT_MSTRINGS);
|
|
}
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* GetWait4MultipleStrResult
|
|
* Description:
|
|
* Retrieves the result from last Wait4MultipleStr call
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* lpszParam - unused
|
|
* Return value:
|
|
* The string, NULL on error
|
|
* Called by:
|
|
* SCCheck
|
|
--*/
|
|
LPCSTR GetWait4MultipleStrResult(PCONNECTINFO pCI, LPCWSTR lpszParam)
|
|
{
|
|
LPCSTR rv = NULL;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (*pCI->szWait4MultipleStrResult)
|
|
rv = (LPCSTR)pCI->szWait4MultipleStrResult;
|
|
else
|
|
rv = NULL;
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* GetFeedbackString
|
|
* Description:
|
|
* Pick a string from connection buffer or wait until
|
|
* something is received
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* result - the buffer for received string
|
|
* max - the buffer size
|
|
* Return value:
|
|
* Error message, NULL on success
|
|
* Called by:
|
|
* * * * EXPORTED * * *
|
|
--*/
|
|
LPCSTR GetFeedbackString(PCONNECTINFO pCI, LPSTR result, UINT max)
|
|
{
|
|
LPCSTR rv = NULL;
|
|
int nFBpos, nFBsize ;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (pCI->dead)
|
|
{
|
|
rv = ERR_CLIENT_IS_DEAD;
|
|
goto exitpt;
|
|
}
|
|
|
|
// Grab the buffer pointers
|
|
EnterCriticalSection(g_lpcsGuardWaitQueue);
|
|
nFBpos = pCI->nFBend + FEEDBACK_SIZE - pCI->nFBsize;
|
|
nFBsize = pCI->nFBsize;
|
|
LeaveCriticalSection(g_lpcsGuardWaitQueue);
|
|
|
|
nFBpos %= FEEDBACK_SIZE;
|
|
|
|
if (!max)
|
|
goto exitpt;
|
|
|
|
*result = 0;
|
|
|
|
if (!nFBsize)
|
|
// Empty buffer, wait for feedback to receive
|
|
{
|
|
rv = _Wait4Str(pCI, L"", WAIT4STR_TIMEOUT, WAIT_STRING);
|
|
}
|
|
if (!rv)
|
|
// Pickup from buffer
|
|
{
|
|
UINT i;
|
|
|
|
EnterCriticalSection(g_lpcsGuardWaitQueue);
|
|
|
|
// Adjust the buffer pointers
|
|
pCI->nFBsize = pCI->nFBend + FEEDBACK_SIZE - nFBpos - 1;
|
|
pCI->nFBsize %= FEEDBACK_SIZE;
|
|
|
|
// Copy the string but watch out for overflow
|
|
if (max > wcslen(pCI->Feedback[nFBpos]) + 1)
|
|
max = wcslen(pCI->Feedback[nFBpos]);
|
|
|
|
for (i = 0; i < max; i++)
|
|
result[i] = (char)(pCI->Feedback[nFBpos][i]);
|
|
result[max] = 0;
|
|
|
|
LeaveCriticalSection(g_lpcsGuardWaitQueue);
|
|
}
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* _SendRClxData
|
|
* Description:
|
|
* Sends request for data to the client
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* pRClxData - data to send
|
|
* Return value:
|
|
* Error message, NULL on success
|
|
* Called by:
|
|
* SCGetClientScreen
|
|
--*/
|
|
LPCSTR
|
|
_SendRClxData(PCONNECTINFO pCI, PRCLXDATA pRClxData)
|
|
{
|
|
LPCSTR rv = NULL;
|
|
PRCLXCONTEXT pRClxCtx;
|
|
RCLXREQPROLOG Request;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!(pCI->RClxMode))
|
|
{
|
|
TRACE((WARNING_MESSAGE, "_SendRClxData: Not in RCLX mode\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
pRClxCtx = (PRCLXCONTEXT)pCI->hClient;
|
|
if (!pRClxCtx || pRClxCtx->hSocket == INVALID_SOCKET)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "Not connected yet, RCLX context is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!pRClxData)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "_SendRClxData: Data block is null\n"));
|
|
rv = ERR_INVALID_PARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
Request.ReqType = REQ_DATA;
|
|
Request.ReqSize = pRClxData->uiSize + sizeof(*pRClxData);
|
|
if (!RClx_SendBuffer(pRClxCtx->hSocket, &Request, sizeof(Request)))
|
|
{
|
|
rv = ERR_CLIENT_DISCONNECTED;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!RClx_SendBuffer(pRClxCtx->hSocket, pRClxData, Request.ReqSize))
|
|
{
|
|
rv = ERR_CLIENT_DISCONNECTED;
|
|
goto exitpt;
|
|
}
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* _Wait4RClxData
|
|
* Description:
|
|
* Waits for data response from RCLX client
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* dwTimeout - timeout value
|
|
* Return value:
|
|
* Error message, NULL on success
|
|
* Called by:
|
|
* SCGetClientScreen
|
|
--*/
|
|
LPCSTR
|
|
_Wait4RClxDataTimeout(PCONNECTINFO pCI, DWORD dwTimeout)
|
|
{
|
|
WAIT4STRING Wait;
|
|
LPCSTR rv = NULL;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!(pCI->RClxMode))
|
|
{
|
|
TRACE((WARNING_MESSAGE, "_Wait4RClxData: Not in RCLX mode\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
memset(&Wait, 0, sizeof(Wait));
|
|
Wait.evWait = CreateEvent(NULL, //security
|
|
TRUE, //manual
|
|
FALSE, //initial state
|
|
NULL); //name
|
|
|
|
Wait.lProcessId = pCI->lProcessId;
|
|
Wait.pOwner = pCI;
|
|
Wait.WaitType = WAIT_DATA;
|
|
|
|
rv = _WaitSomething(pCI, &Wait, dwTimeout);
|
|
if (!rv)
|
|
{
|
|
TRACE(( INFO_MESSAGE, "RCLX data received\n"));
|
|
}
|
|
|
|
CloseHandle(Wait.evWait);
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* _Wait4Str
|
|
* Description:
|
|
* Waits for string(s) with specified timeout
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* lpszParam - waited string(s)
|
|
* dwTimeout - timeout value
|
|
* WaitType - WAIT_STRING ot WAIT_MSTRING
|
|
* Return value:
|
|
* Error message, NULL on success
|
|
* Called by:
|
|
* SCStart, Wait4Str, Wait4StrTimeout, Wait4MultipleStr
|
|
* Wait4MultipleStrTimeout, GetFeedbackString
|
|
--*/
|
|
LPCSTR _Wait4Str(PCONNECTINFO pCI,
|
|
LPCWSTR lpszParam,
|
|
DWORD dwTimeout,
|
|
WAITTYPE WaitType)
|
|
{
|
|
WAIT4STRING Wait;
|
|
int parlen, i;
|
|
LPCSTR rv = NULL;
|
|
|
|
ASSERT(pCI);
|
|
|
|
if (pCI->dead)
|
|
{
|
|
rv = ERR_CLIENT_IS_DEAD;
|
|
goto exitpt;
|
|
}
|
|
|
|
memset(&Wait, 0, sizeof(Wait));
|
|
|
|
// Check the parameter
|
|
parlen = wcslen(lpszParam);
|
|
|
|
// Copy the string
|
|
if (parlen > sizeof(Wait.waitstr)/sizeof(WCHAR)-1)
|
|
parlen = sizeof(Wait.waitstr)/sizeof(WCHAR)-1;
|
|
|
|
wcsncpy(Wait.waitstr, lpszParam, parlen);
|
|
Wait.waitstr[parlen] = 0;
|
|
Wait.strsize = parlen;
|
|
|
|
// Convert delimiters to 0s
|
|
if (WaitType == WAIT_MSTRINGS)
|
|
{
|
|
WCHAR *p = Wait.waitstr;
|
|
|
|
while((p = wcschr(p, WAIT_STR_DELIMITER)))
|
|
{
|
|
*p = 0;
|
|
p++;
|
|
}
|
|
}
|
|
|
|
Wait.evWait = CreateEvent(NULL, //security
|
|
TRUE, //manual
|
|
FALSE, //initial state
|
|
NULL); //name
|
|
|
|
if (!Wait.evWait) {
|
|
TRACE((ERROR_MESSAGE, "Couldn't create event\n"));
|
|
goto exitpt;
|
|
}
|
|
Wait.lProcessId = pCI->lProcessId;
|
|
Wait.pOwner = pCI;
|
|
Wait.WaitType = WaitType;
|
|
|
|
TRACE(( INFO_MESSAGE, "Expecting string: %S\n", Wait.waitstr));
|
|
rv = _WaitSomething(pCI, &Wait, dwTimeout);
|
|
TRACE(( INFO_MESSAGE, "String %S received\n", Wait.waitstr));
|
|
|
|
if (!rv && pCI->dead)
|
|
{
|
|
rv = ERR_CLIENT_IS_DEAD;
|
|
}
|
|
|
|
CloseHandle(Wait.evWait);
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* _WaitSomething
|
|
* Description:
|
|
* Wait for some event: string, connect or disconnect
|
|
* Meanwhile checks for chat sequences
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* pWait - the event function waits for
|
|
* dwTimeout - timeout value
|
|
* Return value:
|
|
* Error message, NULL on success
|
|
* Called by:
|
|
* Wait4Connect, Wait4Disconnect, _Wait4Str
|
|
--*/
|
|
LPCSTR
|
|
_WaitSomething(PCONNECTINFO pCI, PWAIT4STRING pWait, DWORD dwTimeout)
|
|
{
|
|
BOOL bDone = FALSE;
|
|
LPCSTR rv = NULL;
|
|
DWORD waitres;
|
|
|
|
ASSERT(pCI || pWait);
|
|
|
|
_AddToWaitQueue(pCI, pWait);
|
|
pCI->evWait4Str = pWait->evWait;
|
|
|
|
do {
|
|
if (
|
|
(waitres = WaitForMultipleObjects(
|
|
pCI->nChatNum+1,
|
|
&pCI->evWait4Str,
|
|
FALSE,
|
|
dwTimeout)) <= (pCI->nChatNum + WAIT_OBJECT_0))
|
|
{
|
|
if (waitres == WAIT_OBJECT_0)
|
|
{
|
|
bDone = TRUE;
|
|
} else {
|
|
PWAIT4STRING pNWait;
|
|
|
|
ASSERT((unsigned)pCI->nChatNum >= waitres - WAIT_OBJECT_0);
|
|
|
|
// Here we must send response messages
|
|
waitres -= WAIT_OBJECT_0 + 1;
|
|
ResetEvent(pCI->aevChatSeq[waitres]);
|
|
pNWait = _RetrieveFromWaitQByEvent(pCI->aevChatSeq[waitres]);
|
|
|
|
ASSERT(pNWait);
|
|
ASSERT(wcslen(pNWait->respstr));
|
|
TRACE((INFO_MESSAGE,
|
|
"Recieved : [%d]%S\n",
|
|
pNWait->strsize,
|
|
pNWait->waitstr ));
|
|
SCSendtextAsMsgs(pCI, (LPCWSTR)pNWait->respstr);
|
|
}
|
|
} else {
|
|
if (*(pWait->waitstr))
|
|
{
|
|
TRACE((WARNING_MESSAGE,
|
|
"Wait for \"%S\" failed: TIMEOUT\n",
|
|
pWait->waitstr));
|
|
} else {
|
|
TRACE((WARNING_MESSAGE, "Wait failed: TIMEOUT\n"));
|
|
}
|
|
rv = ERR_WAIT_FAIL_TIMEOUT;
|
|
bDone = TRUE;
|
|
}
|
|
} while(!bDone);
|
|
|
|
pCI->evWait4Str = NULL;
|
|
|
|
_RemoveFromWaitQueue(pWait);
|
|
|
|
if (!rv && pCI->dead)
|
|
rv = ERR_CLIENT_IS_DEAD;
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* RegisterChat
|
|
* Description:
|
|
* This regiters a wait4str <-> sendtext pair
|
|
* so when we receive a specific string we will send a proper messages
|
|
* lpszParam is kind of: XXXXXX<->YYYYYY
|
|
* XXXXX is the waited string, YYYYY is the respond
|
|
* These command could be nested up to: MAX_WAITING_EVENTS
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* lpszParam - parameter, example:
|
|
* "Connect to existing Windows NT session<->\n"
|
|
* - hit enter when this string is received
|
|
* Return value:
|
|
* Error message, NULL on success
|
|
* Called by:
|
|
* SCCheck, _Login
|
|
--*/
|
|
LPCSTR RegisterChat(PCONNECTINFO pCI, LPCWSTR lpszParam)
|
|
{
|
|
PWAIT4STRING pWait;
|
|
int parlen, i, resplen;
|
|
LPCSTR rv = NULL;
|
|
LPCWSTR resp;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!lpszParam)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Parameter is null\n"));
|
|
rv = ERR_INVALID_PARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (pCI->dead)
|
|
{
|
|
rv = ERR_CLIENT_IS_DEAD;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (pCI->nChatNum >= MAX_WAITING_EVENTS)
|
|
{
|
|
TRACE(( WARNING_MESSAGE, "RegisterChat: too much waiting strings\n" ));
|
|
goto exitpt;
|
|
}
|
|
|
|
// Split the parameter
|
|
resp = wcsstr(lpszParam, CHAT_SEPARATOR);
|
|
// Check the strings
|
|
if (!resp)
|
|
{
|
|
TRACE(( WARNING_MESSAGE, "RegisterChat: invalid parameter\n" ));
|
|
rv = ERR_INVALID_PARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
parlen = wcslen(lpszParam) - wcslen(resp);
|
|
resp += wcslen(CHAT_SEPARATOR);
|
|
|
|
if (!parlen)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "RegisterChat empty parameter\n"));
|
|
goto exitpt;
|
|
}
|
|
|
|
resplen = wcslen(resp);
|
|
if (!resplen)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "RegisterChat: empty respond string\n" ));
|
|
goto exitpt;
|
|
}
|
|
|
|
// Allocate the WAIT4STRING structure
|
|
pWait = (PWAIT4STRING)malloc(sizeof(*pWait));
|
|
if (!pWait)
|
|
{
|
|
TRACE((WARNING_MESSAGE,
|
|
"RegisterChat: can't allocate %d bytes\n",
|
|
sizeof(*pWait) ));
|
|
goto exitpt;
|
|
}
|
|
memset(pWait, 0, sizeof(*pWait));
|
|
|
|
// Copy the waited string
|
|
if (parlen > sizeof(pWait->waitstr)/sizeof(WCHAR)-1)
|
|
parlen = sizeof(pWait->waitstr)/sizeof(WCHAR)-1;
|
|
|
|
wcsncpy(pWait->waitstr, lpszParam, parlen);
|
|
pWait->waitstr[parlen] = 0;
|
|
pWait->strsize = parlen;
|
|
|
|
// Copy the respond string
|
|
if (resplen > sizeof(pWait->respstr)-1)
|
|
resplen = sizeof(pWait->respstr)-1;
|
|
|
|
wcsncpy(pWait->respstr, resp, resplen);
|
|
pWait->respstr[resplen] = 0;
|
|
pWait->respsize = resplen;
|
|
|
|
pWait->evWait = CreateEvent(NULL, //security
|
|
TRUE, //manual
|
|
FALSE, //initial state
|
|
NULL); //name
|
|
|
|
if (!pWait->evWait) {
|
|
TRACE((ERROR_MESSAGE, "Couldn't create event\n"));
|
|
free (pWait);
|
|
goto exitpt;
|
|
}
|
|
pWait->lProcessId = pCI->lProcessId;
|
|
pWait->pOwner = pCI;
|
|
pWait->WaitType = WAIT_STRING;
|
|
|
|
// _AddToWaitQNoCheck(pCI, pWait);
|
|
_AddToWaitQueue(pCI, pWait);
|
|
|
|
// Add to connection info array
|
|
pCI->aevChatSeq[pCI->nChatNum] = pWait->evWait;
|
|
pCI->nChatNum++;
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
// Remove a WAIT4STRING from waiting Q
|
|
// Param is the waited string
|
|
/*++
|
|
* Function:
|
|
* UnregisterChat
|
|
* Description:
|
|
* Deallocates and removes from waiting Q everithing
|
|
* from RegisterChat function
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* lpszParam - waited string
|
|
* Return value:
|
|
* Error message, NULL on success
|
|
* Called by:
|
|
* SCCheck, _Login
|
|
--*/
|
|
LPCSTR UnregisterChat(PCONNECTINFO pCI, LPCWSTR lpszParam)
|
|
{
|
|
PWAIT4STRING pWait;
|
|
LPCSTR rv = NULL;
|
|
int i;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!lpszParam)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Parameter is null\n"));
|
|
rv = ERR_INVALID_PARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
pWait = _RemoveFromWaitQIndirect(pCI, lpszParam);
|
|
if (!pWait)
|
|
{
|
|
TRACE((WARNING_MESSAGE,
|
|
"UnregisterChat: can't find waiting string: %S\n",
|
|
lpszParam ));
|
|
goto exitpt;
|
|
}
|
|
|
|
i = 0;
|
|
while (i < pCI->nChatNum && pCI->aevChatSeq[i] != pWait->evWait)
|
|
i++;
|
|
|
|
ASSERT(i < pCI->nChatNum);
|
|
|
|
memmove(pCI->aevChatSeq+i,
|
|
pCI->aevChatSeq+i+1,
|
|
(pCI->nChatNum-i-1)*sizeof(pCI->aevChatSeq[0]));
|
|
pCI->nChatNum--;
|
|
|
|
CloseHandle(pWait->evWait);
|
|
|
|
free(pWait);
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Returns TRUE if the client is dead
|
|
*/
|
|
PROTOCOLAPI
|
|
BOOL
|
|
SMCAPI
|
|
SCIsDead(PCONNECTINFO pCI)
|
|
{
|
|
if (!pCI)
|
|
return TRUE;
|
|
|
|
return pCI->dead;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* _CloseConnectInfo
|
|
* Description:
|
|
* Clean all resources for this connection. Close the client
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* Called by:
|
|
* SCDisconnect
|
|
--*/
|
|
VOID
|
|
_CloseConnectInfo(PCONNECTINFO pCI)
|
|
{
|
|
PRCLXDATACHAIN pRClxDataChain, pNext;
|
|
|
|
ASSERT(pCI);
|
|
|
|
_FlushFromWaitQ(pCI);
|
|
|
|
// Close All handles
|
|
EnterCriticalSection(g_lpcsGuardWaitQueue);
|
|
|
|
/* // not needed, the handle is already closed
|
|
if (pCI->evWait4Str)
|
|
{
|
|
CloseHandle(pCI->evWait4Str);
|
|
pCI->evWait4Str = NULL;
|
|
}
|
|
*/
|
|
|
|
// Chat events are already closed by FlushFromWaitQ
|
|
// no need to close them
|
|
|
|
pCI->nChatNum = 0;
|
|
|
|
if (!pCI->RClxMode)
|
|
{
|
|
// The client was local, so we have handles opened
|
|
if (pCI->hProcess)
|
|
CloseHandle(pCI->hProcess);
|
|
if (pCI->hThread);
|
|
CloseHandle(pCI->hThread);
|
|
|
|
pCI->hProcess = pCI->hThread =NULL;
|
|
} else {
|
|
// Hmmm, RCLX mode. Then disconnect the socket
|
|
|
|
if (pCI->hClient)
|
|
RClx_EndRecv((PRCLXCONTEXT)(pCI->hClient));
|
|
|
|
pCI->hClient = NULL; // Clean the pointer
|
|
}
|
|
|
|
// Clear the clipboard handle (if any)
|
|
if (pCI->ghClipboard)
|
|
{
|
|
GlobalFree(pCI->ghClipboard);
|
|
pCI->ghClipboard = NULL;
|
|
}
|
|
|
|
// clear any recevied RCLX data
|
|
pRClxDataChain = pCI->pRClxDataChain;
|
|
while(pRClxDataChain)
|
|
{
|
|
pNext = pRClxDataChain->pNext;
|
|
free(pRClxDataChain);
|
|
pRClxDataChain = pNext;
|
|
}
|
|
|
|
LeaveCriticalSection(g_lpcsGuardWaitQueue);
|
|
|
|
if (!pCI->RClxMode)
|
|
_DeleteClientRegistry(pCI);
|
|
|
|
free(pCI);
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* _Login
|
|
* Description:
|
|
* Emulate login procedure
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* lpszUserName - user name
|
|
* lpszPassword - password
|
|
* lpszDomain - domain name
|
|
* Return value:
|
|
* Error message, NULL on success
|
|
* Called by:
|
|
* SCConnect
|
|
--*/
|
|
LPCSTR _Login(PCONNECTINFO pCI,
|
|
LPCWSTR lpszUserName,
|
|
LPCWSTR lpszPassword,
|
|
LPCWSTR lpszDomain)
|
|
{
|
|
LPCSTR waitres;
|
|
LPCSTR rv = NULL;
|
|
WCHAR szBuff[MAX_STRING_LENGTH];
|
|
INT nLogonRetrys = 5;
|
|
UINT nLogonWaitTime;
|
|
|
|
ASSERT(pCI);
|
|
|
|
retry_logon:
|
|
_snwprintf(szBuff, MAX_STRING_LENGTH, L"%s|%s|%s",
|
|
g_strWinlogon, g_strPriorWinlogon, g_strLogonDisabled);
|
|
|
|
waitres = Wait4MultipleStr(pCI, szBuff);
|
|
if (!waitres)
|
|
{
|
|
if (pCI->nWait4MultipleStrResult == 1)
|
|
{
|
|
SCSendtextAsMsgs(pCI, g_strPriorWinlogon_Act);
|
|
waitres = Wait4Str(pCI, g_strWinlogon);
|
|
}
|
|
else if (pCI->nWait4MultipleStrResult == 2)
|
|
{
|
|
SCSendtextAsMsgs(pCI, L"\\n");
|
|
waitres = Wait4Str(pCI, g_strWinlogon);
|
|
}
|
|
}
|
|
|
|
if (waitres)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Login failed"));
|
|
rv = waitres;
|
|
goto exitpt;
|
|
}
|
|
|
|
// Hit Alt+U to go to user name field
|
|
SCSendtextAsMsgs(pCI, g_strWinlogon_Act);
|
|
|
|
SCSendtextAsMsgs(pCI, lpszUserName);
|
|
// Hit <Tab> key
|
|
Sleep(300);
|
|
SCSendtextAsMsgs(pCI, L"\\t");
|
|
|
|
SCSendtextAsMsgs(pCI, lpszPassword);
|
|
// Hit <Tab> key
|
|
Sleep(300);
|
|
SCSendtextAsMsgs(pCI, L"\\t");
|
|
|
|
SCSendtextAsMsgs(pCI, lpszDomain);
|
|
Sleep(300);
|
|
|
|
// Retry logon in case of
|
|
// 1. Winlogon is on background
|
|
// 2. Wrong username/password/domain
|
|
// 3. Other
|
|
|
|
// Hit <Enter>
|
|
SCSendtextAsMsgs(pCI, L"\\n");
|
|
|
|
nLogonWaitTime = 0;
|
|
while (!pCI->dead && !pCI->uiSessionId && nLogonWaitTime < CONNECT_TIMEOUT)
|
|
{
|
|
// Sleep with wait otherwise the chat won't work
|
|
// i.e. this is a hack
|
|
waitres = _Wait4Str(pCI, g_strLogonErrorMessage, 1000, WAIT_STRING);
|
|
if (!waitres)
|
|
// Error message received
|
|
{
|
|
SCSendtextAsMsgs(pCI, L"\\n");
|
|
Sleep(1000);
|
|
break;
|
|
}
|
|
nLogonWaitTime += 1000;
|
|
}
|
|
|
|
if (!pCI->dead && !pCI->uiSessionId)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Logon sequence failed. Retrying (%d)",
|
|
nLogonRetrys));
|
|
if (nLogonRetrys--)
|
|
goto retry_logon;
|
|
}
|
|
|
|
if (!pCI->uiSessionId)
|
|
{
|
|
// Send Enter, just in case we are not logged yet
|
|
SCSendtextAsMsgs(pCI, L"\\n");
|
|
rv = ERR_CANTLOGON;
|
|
}
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
WPARAM _GetVirtualKey(INT scancode)
|
|
{
|
|
if (scancode == 29) // L Control
|
|
return VK_CONTROL;
|
|
else if (scancode == 42) // L Shift
|
|
return VK_SHIFT;
|
|
else if (scancode == 56) // L Alt
|
|
return VK_MENU;
|
|
else
|
|
return MapVirtualKey(scancode, 3);
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* SCSendtextAsMsgs
|
|
* Description:
|
|
* Converts a string to WM_KEYUP/KEYDOWN messages
|
|
* And sends them thru client window
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* lpszString - the string to be send
|
|
* it can contain the following escape character:
|
|
* \n - Enter, \t - Tab, \^ - Esc, \& - Alt switch up/down
|
|
* \XXX - scancode XXX is down, \*XXX - scancode XXX is up
|
|
* Return value:
|
|
* Error message, NULL on success
|
|
* Called by:
|
|
* SCLogoff, SCStart, _WaitSomething, _Login
|
|
--*/
|
|
PROTOCOLAPI
|
|
LPCSTR
|
|
SMCAPI
|
|
SCSendtextAsMsgs(PCONNECTINFO pCI, LPCWSTR lpszString)
|
|
{
|
|
LPCSTR rv = NULL;
|
|
INT scancode = 0;
|
|
WPARAM vkKey;
|
|
BOOL bShiftDown = FALSE;
|
|
BOOL bAltKey = FALSE;
|
|
BOOL bCtrlKey = FALSE;
|
|
UINT uiMsg;
|
|
LPARAM lParam;
|
|
|
|
#define _SEND_KEY(_c_, _m_, _v_, _l_) {/*Sleep(40); */SCSenddata(_c_, _m_, _v_, _l_);}
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!lpszString)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "NULL pointer passed to SCSendtextAsMsgs"));
|
|
rv = ERR_INVALID_PARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
TRACE(( INFO_MESSAGE, "Sending: \"%S\"\n", lpszString));
|
|
|
|
for (;*lpszString; lpszString++)
|
|
{
|
|
if (*lpszString != '\\') {
|
|
try_again:
|
|
if ((scancode = OemKeyScan(*lpszString)) == 0xffffffff)
|
|
{
|
|
rv = ERR_INVALID_SCANCODE_IN_XLAT;
|
|
goto exitpt;
|
|
}
|
|
|
|
// Check the Shift key state
|
|
if ((scancode & SHIFT_DOWN) && !bShiftDown)
|
|
{
|
|
uiMsg = (bAltKey)?WM_SYSKEYDOWN:WM_KEYDOWN;
|
|
_SEND_KEY(pCI, uiMsg, VK_SHIFT,
|
|
WM_KEY_LPARAM(1, 0x2A, 0, (bAltKey)?1:0, 0, 0));
|
|
bShiftDown = TRUE;
|
|
}
|
|
else if (!(scancode & SHIFT_DOWN) && bShiftDown)
|
|
{
|
|
uiMsg = (bAltKey)?WM_SYSKEYUP:WM_KEYUP;
|
|
_SEND_KEY(pCI, uiMsg, VK_SHIFT,
|
|
WM_KEY_LPARAM(1, 0x2A, 0, (bAltKey)?1:0, 1, 1));
|
|
bShiftDown = FALSE;
|
|
}
|
|
} else {
|
|
// Non printable symbols
|
|
lpszString++;
|
|
switch(*lpszString)
|
|
{
|
|
case 'n': scancode = 0x1C; break; // Enter
|
|
case 't': scancode = 0x0F; break; // Tab
|
|
case '^': scancode = 0x01; break; // Esc
|
|
case 'p': Sleep(100); break; // Sleep for 0.1 sec
|
|
case 'P': Sleep(1000); break; // Sleep for 1 sec
|
|
case 'x': SCSendMouseClick(pCI, pCI->xRes/2, pCI->yRes/2); break;
|
|
case '&':
|
|
// Alt key
|
|
if (bAltKey)
|
|
{
|
|
_SEND_KEY(pCI, WM_KEYUP, VK_MENU,
|
|
WM_KEY_LPARAM(1, 0x38, 0, 0, 1, 1));
|
|
} else {
|
|
_SEND_KEY(pCI, WM_SYSKEYDOWN, VK_MENU,
|
|
WM_KEY_LPARAM(1, 0x38, 0, 1, 0, 0));
|
|
}
|
|
bAltKey = !bAltKey;
|
|
continue;
|
|
case '*':
|
|
lpszString ++;
|
|
if (isdigit(*lpszString))
|
|
{
|
|
INT exten;
|
|
|
|
scancode = _wtoi(lpszString);
|
|
TRACE((INFO_MESSAGE, "Scancode: %d UP\n", scancode));
|
|
|
|
vkKey = _GetVirtualKey(scancode);
|
|
|
|
uiMsg = (!bAltKey || bCtrlKey)?WM_KEYUP:WM_SYSKEYUP;
|
|
|
|
if (vkKey == VK_MENU)
|
|
bAltKey = FALSE;
|
|
else if (vkKey == VK_CONTROL)
|
|
bCtrlKey = FALSE;
|
|
else if (vkKey == VK_SHIFT)
|
|
bShiftDown = FALSE;
|
|
|
|
exten = (_IsExtendedScanCode(scancode))?1:0;
|
|
lParam = WM_KEY_LPARAM(1, scancode, exten, (bAltKey)?1:0, 1, 1);
|
|
if (uiMsg == WM_KEYUP)
|
|
{
|
|
TRACE((INFO_MESSAGE, "WM_KEYUP, 0x%x, 0x%x\n", vkKey, lParam));
|
|
} else {
|
|
TRACE((INFO_MESSAGE, "WM_SYSKEYUP, 0x%x, 0x%x\n", vkKey, lParam));
|
|
}
|
|
|
|
_SEND_KEY(pCI, uiMsg, vkKey, lParam);
|
|
|
|
|
|
while(isdigit(lpszString[1]))
|
|
lpszString++;
|
|
} else {
|
|
lpszString--;
|
|
}
|
|
continue;
|
|
break;
|
|
case 0: continue;
|
|
default:
|
|
if (isdigit(*lpszString))
|
|
{
|
|
INT exten;
|
|
|
|
scancode = _wtoi(lpszString);
|
|
TRACE((INFO_MESSAGE, "Scancode: %d DOWN\n", scancode));
|
|
vkKey = _GetVirtualKey(scancode);
|
|
|
|
if (vkKey == VK_MENU)
|
|
bAltKey = TRUE;
|
|
else if (vkKey == VK_CONTROL)
|
|
bCtrlKey = TRUE;
|
|
else if (vkKey == VK_SHIFT)
|
|
bShiftDown = TRUE;
|
|
|
|
uiMsg = (!bAltKey || bCtrlKey)?WM_KEYDOWN:WM_SYSKEYDOWN;
|
|
|
|
exten = (_IsExtendedScanCode(scancode))?1:0;
|
|
lParam = WM_KEY_LPARAM(1, scancode, exten, (bAltKey)?1:0, 0, 0);
|
|
|
|
if (uiMsg == WM_KEYDOWN)
|
|
{
|
|
TRACE((INFO_MESSAGE, "WM_KEYDOWN, 0x%x, 0x%x\n", vkKey, lParam));
|
|
} else {
|
|
TRACE((INFO_MESSAGE, "WM_SYSKEYDOWN, 0x%x, 0x%x\n", vkKey, lParam));
|
|
}
|
|
|
|
_SEND_KEY(pCI, uiMsg, vkKey, lParam);
|
|
|
|
while(isdigit(lpszString[1]))
|
|
lpszString++;
|
|
|
|
continue;
|
|
}
|
|
goto try_again;
|
|
}
|
|
|
|
}
|
|
vkKey = MapVirtualKey(scancode, 3);
|
|
// Remove flag fields
|
|
scancode &= 0xff;
|
|
|
|
uiMsg = (!bAltKey || bCtrlKey)?WM_KEYDOWN:WM_SYSKEYDOWN;
|
|
// Send the scancode
|
|
_SEND_KEY(pCI, uiMsg, vkKey,
|
|
WM_KEY_LPARAM(1, scancode, 0, (bAltKey)?1:0, 0, 0));
|
|
uiMsg = (!bAltKey || bCtrlKey)?WM_KEYUP:WM_SYSKEYUP;
|
|
_SEND_KEY(pCI, uiMsg, vkKey,
|
|
WM_KEY_LPARAM(1, scancode, 0, (bAltKey)?1:0, 1, 1));
|
|
}
|
|
|
|
// And Alt key
|
|
if (bAltKey)
|
|
_SEND_KEY(pCI, WM_KEYUP, VK_MENU,
|
|
WM_KEY_LPARAM(1, 0x38, 0, 0, 1, 1));
|
|
|
|
// Shift up
|
|
if (bShiftDown)
|
|
_SEND_KEY(pCI, WM_KEYUP, VK_LSHIFT,
|
|
WM_KEY_LPARAM(1, 0x2A, 0, 0, 1, 1));
|
|
|
|
// Ctrl key
|
|
if (bCtrlKey)
|
|
_SEND_KEY(pCI, WM_KEYUP, VK_CONTROL,
|
|
WM_KEY_LPARAM(1, 0x1D, 0, 0, 1, 1));
|
|
#undef _SEND_KEY
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* SwitchToProcess
|
|
* Description:
|
|
* Use Alt+Tab to switch to a particular process that is already running
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* lpszParam - the text in the alt-tab box that uniquely identifies the
|
|
* process we should stop at (i.e., end up switching to)
|
|
* Return value:
|
|
* Error message, NULL on success
|
|
* Called by:
|
|
* SCCheck
|
|
--*/
|
|
PROTOCOLAPI
|
|
LPCSTR
|
|
SMCAPI
|
|
SCSwitchToProcess(PCONNECTINFO pCI, LPCWSTR lpszParam)
|
|
{
|
|
#define ALT_TAB_WAIT_TIMEOUT 1000
|
|
#define MAX_APPS 20
|
|
|
|
LPCSTR rv = NULL;
|
|
LPCSTR waitres = NULL;
|
|
INT retrys = MAX_APPS;
|
|
|
|
WCHAR *wszCurrTask = 0;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (pCI->dead)
|
|
{
|
|
rv = ERR_CLIENT_IS_DEAD;
|
|
goto exitpt;
|
|
}
|
|
|
|
|
|
// Wait and look for the string, before we do any switching. This makes
|
|
// sure we don't hit the string even before we hit alt-tab, and then
|
|
// end up switching to the wrong process
|
|
|
|
while (_Wait4Str(pCI, lpszParam, ALT_TAB_WAIT_TIMEOUT/5, WAIT_STRING) == 0)
|
|
;
|
|
|
|
// Press alt down
|
|
SCSenddata(pCI, WM_KEYDOWN, 18, 540540929);
|
|
|
|
// Now loop through the list of applications (assuming there is one),
|
|
// stopping at our desired app.
|
|
do {
|
|
SCSenddata(pCI, WM_KEYDOWN, 9, 983041);
|
|
SCSenddata(pCI, WM_KEYUP, 9, -1072758783);
|
|
|
|
|
|
waitres = _Wait4Str(pCI, lpszParam, ALT_TAB_WAIT_TIMEOUT, WAIT_STRING);
|
|
|
|
retrys --;
|
|
} while (waitres && retrys);
|
|
|
|
SCSenddata(pCI, WM_KEYUP, 18, -1070071807);
|
|
|
|
rv = waitres;
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* SCSetClientTopmost
|
|
* Description:
|
|
* Swithces the focus to this client
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* lpszParam
|
|
* - "0" will remote the WS_EX_TOPMOST style
|
|
* - "non_zero" will set it as topmost window
|
|
* Return value:
|
|
* Error message, NULL on success
|
|
* Called by:
|
|
* SCCheck
|
|
--*/
|
|
PROTOCOLAPI
|
|
LPCSTR
|
|
SMCAPI
|
|
SCSetClientTopmost(
|
|
PCONNECTINFO pCI,
|
|
LPCWSTR lpszParam
|
|
)
|
|
{
|
|
LPCSTR rv = NULL;
|
|
BOOL bTop = FALSE;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (pCI->dead)
|
|
{
|
|
rv = ERR_CLIENT_IS_DEAD;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (pCI->RClxMode)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "SetClientOnFocus not supported in RCLX mode\n"));
|
|
rv = ERR_NOTSUPPORTED;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!pCI->hClient)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Client's window handle is null\n"));
|
|
rv = ERR_INVALID_PARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (lpszParam)
|
|
bTop = (_wtoi(lpszParam) != 0);
|
|
else
|
|
bTop = 0;
|
|
|
|
SetWindowPos(pCI->hClient,
|
|
(bTop)?HWND_TOPMOST:HWND_NOTOPMOST,
|
|
0,0,0,0,
|
|
SWP_NOMOVE | SWP_NOSIZE);
|
|
|
|
ShowWindow(pCI->hClient, SW_SHOWNORMAL);
|
|
|
|
if (bTop)
|
|
{
|
|
TRACE((INFO_MESSAGE, "Client is SET as topmost window\n"));
|
|
} else {
|
|
TRACE((INFO_MESSAGE, "Client is RESET as topmost window\n"));
|
|
}
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* _SendMouseClick
|
|
* Description:
|
|
* Sends a messages for a mouse click
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* xPos - mouse position
|
|
* yPos
|
|
* Return value:
|
|
* error string if fails, NULL on success
|
|
* Called by:
|
|
* * * * EXPORTED * * *
|
|
--*/
|
|
PROTOCOLAPI
|
|
LPCSTR
|
|
SMCAPI
|
|
SCSendMouseClick(
|
|
PCONNECTINFO pCI,
|
|
UINT xPos,
|
|
UINT yPos)
|
|
{
|
|
LPCSTR rv;
|
|
|
|
rv = SCSenddata(pCI, WM_LBUTTONDOWN, 0, xPos + (yPos << 16));
|
|
if (!rv)
|
|
SCSenddata(pCI, WM_LBUTTONUP, 0, xPos + (yPos << 16));
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* SCSaveClientScreen
|
|
* Description:
|
|
* Saves in a file rectangle of the client's receive screen buffer
|
|
* ( aka shadow bitmap)
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* left, top, right, bottom - rectangle coordinates
|
|
* if all == -1 get's the whole screen
|
|
* szFileName - file to record
|
|
* Return value:
|
|
* error string if fails, NULL on success
|
|
* Called by:
|
|
* * * * EXPORTED * * *
|
|
--*/
|
|
PROTOCOLAPI
|
|
LPCSTR
|
|
SMCAPI
|
|
SCSaveClientScreen(
|
|
PCONNECTINFO pCI,
|
|
INT left,
|
|
INT top,
|
|
INT right,
|
|
INT bottom,
|
|
LPCSTR szFileName)
|
|
{
|
|
LPCSTR rv = NULL;
|
|
PVOID pDIB = NULL;
|
|
UINT uiSize = 0;
|
|
|
|
if (!szFileName)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "SCSaveClientScreen: szFileName is NULL\n"));
|
|
rv = ERR_INVALID_PARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
// leave the rest of param checking to SCGetClientScreen
|
|
rv = SCGetClientScreen(pCI, left, top, right, bottom, &uiSize, &pDIB);
|
|
if (rv)
|
|
goto exitpt;
|
|
|
|
if (!pDIB || !uiSize)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "SCSaveClientScreen: failed, no data\n"));
|
|
rv = ERR_NODATA;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!SaveDIB(pDIB, szFileName))
|
|
{
|
|
TRACE((ERROR_MESSAGE, "SCSaveClientScreen: save failed\n"));
|
|
rv = ERR_NODATA;
|
|
goto exitpt;
|
|
}
|
|
|
|
exitpt:
|
|
|
|
if (pDIB)
|
|
free(pDIB);
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* SCGetClientScreen
|
|
* Description:
|
|
* Gets rectangle of the client's receive screen buffer
|
|
* ( aka shadow bitmap)
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* left, top, right, bottom - rectangle coordinates
|
|
* if all == -1 get's the whole screen
|
|
* ppDIB - pointer to the received DIB
|
|
* puiSize - size of allocated data in ppDIB
|
|
*
|
|
* !!!!! DON'T FORGET to free() THAT MEMORY !!!!!
|
|
*
|
|
* Return value:
|
|
* error string if fails, NULL on success
|
|
* Called by:
|
|
* SCSaveClientScreen
|
|
* * * * EXPORTED * * *
|
|
--*/
|
|
PROTOCOLAPI
|
|
LPCSTR
|
|
SMCAPI
|
|
SCGetClientScreen(
|
|
PCONNECTINFO pCI,
|
|
INT left,
|
|
INT top,
|
|
INT right,
|
|
INT bottom,
|
|
UINT *puiSize,
|
|
PVOID *ppDIB)
|
|
{
|
|
LPCSTR rv;
|
|
PRCLXDATA pRClxData;
|
|
PREQBITMAP pReqBitmap;
|
|
PRCLXDATACHAIN pIter, pPrev, pNext;
|
|
PRCLXDATACHAIN pRClxDataChain = NULL;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (pCI->dead)
|
|
{
|
|
rv = ERR_CLIENT_IS_DEAD;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!pCI->RClxMode)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "SCGetClientScreen is not supported in non-RCLX mode\n"));
|
|
rv = ERR_NOTSUPPORTED;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!ppDIB || !puiSize)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "ppDIB and/or puiSize parameter is NULL\n"));
|
|
rv = ERR_INVALID_PARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
// Remove all recieved DATA_BITMAP from the recieve buffer
|
|
EnterCriticalSection(g_lpcsGuardWaitQueue);
|
|
{
|
|
pIter = pCI->pRClxDataChain;
|
|
pPrev = NULL;
|
|
|
|
while (pIter)
|
|
{
|
|
pNext = pIter->pNext;
|
|
|
|
if (pIter->RClxData.uiType == DATA_BITMAP)
|
|
{
|
|
// dispose this entry
|
|
if (pPrev)
|
|
pPrev->pNext = pIter->pNext;
|
|
else
|
|
pCI->pRClxDataChain = pIter->pNext;
|
|
|
|
if (!pIter->pNext)
|
|
pCI->pRClxLastDataChain = pPrev;
|
|
|
|
free(pIter);
|
|
} else
|
|
pPrev = pIter;
|
|
|
|
pIter = pNext;
|
|
}
|
|
}
|
|
LeaveCriticalSection(g_lpcsGuardWaitQueue);
|
|
|
|
pRClxData = alloca(sizeof(*pRClxData) + sizeof(*pReqBitmap));
|
|
pRClxData->uiType = DATA_BITMAP;
|
|
pRClxData->uiSize = sizeof(*pReqBitmap);
|
|
pReqBitmap = (PREQBITMAP)pRClxData->Data;
|
|
pReqBitmap->left = left;
|
|
pReqBitmap->top = top;
|
|
pReqBitmap->right = right;
|
|
pReqBitmap->bottom = bottom;
|
|
|
|
TRACE((INFO_MESSAGE, "Getting client's DIB (%d, %d, %d, %d)\n", left, top, right, bottom));
|
|
rv = _SendRClxData(pCI, pRClxData);
|
|
|
|
if (rv)
|
|
goto exitpt;
|
|
|
|
do {
|
|
rv = _Wait4RClxDataTimeout(pCI, WAIT4STR_TIMEOUT);
|
|
if (rv)
|
|
goto exitpt;
|
|
|
|
if (!pCI->pRClxDataChain)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "RClxData is not received\n"));
|
|
rv = ERR_WAIT_FAIL_TIMEOUT;
|
|
goto exitpt;
|
|
}
|
|
|
|
EnterCriticalSection(g_lpcsGuardWaitQueue);
|
|
// Get any received DATA_BITMAP
|
|
{
|
|
pIter = pCI->pRClxDataChain;
|
|
pPrev = NULL;
|
|
|
|
while (pIter)
|
|
{
|
|
pNext = pIter->pNext;
|
|
|
|
if (pIter->RClxData.uiType == DATA_BITMAP)
|
|
{
|
|
// dispose this entry from the chain
|
|
if (pPrev)
|
|
pPrev->pNext = pIter->pNext;
|
|
else
|
|
pCI->pRClxDataChain = pIter->pNext;
|
|
|
|
if (!pIter->pNext)
|
|
pCI->pRClxLastDataChain = pPrev;
|
|
|
|
goto entry_is_found;
|
|
} else
|
|
pPrev = pIter;
|
|
|
|
pIter = pNext;
|
|
}
|
|
|
|
entry_is_found:
|
|
pRClxDataChain = (pIter && pIter->RClxData.uiType == DATA_BITMAP)?
|
|
pIter:NULL;
|
|
}
|
|
LeaveCriticalSection(g_lpcsGuardWaitQueue);
|
|
} while (!pRClxDataChain && !pCI->dead);
|
|
|
|
if (!pRClxDataChain)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "SCGetClientScreen: client died\n"));
|
|
goto exitpt;
|
|
}
|
|
|
|
*ppDIB = malloc(pRClxDataChain->RClxData.uiSize);
|
|
if (!(*ppDIB))
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Can't allocate %d bytes\n",
|
|
pRClxDataChain->RClxData.uiSize));
|
|
rv = ERR_ALLOCATING_MEMORY;
|
|
goto exitpt;
|
|
}
|
|
|
|
memcpy(*ppDIB,
|
|
pRClxDataChain->RClxData.Data,
|
|
pRClxDataChain->RClxData.uiSize);
|
|
*puiSize = pRClxDataChain->RClxData.uiSize;
|
|
|
|
exitpt:
|
|
|
|
if (pRClxDataChain)
|
|
free(pRClxDataChain);
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* SCSendVCData
|
|
* Description:
|
|
* Sends data to a virtual channel
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* szVCName - the virtual channel name
|
|
* pData - data
|
|
* uiSize - data size
|
|
* Return value:
|
|
* error string if fails, NULL on success
|
|
* Called by:
|
|
* * * * EXPORTED * * *
|
|
--*/
|
|
PROTOCOLAPI
|
|
LPCSTR
|
|
SMCAPI
|
|
SCSendVCData(
|
|
PCONNECTINFO pCI,
|
|
LPCSTR szVCName,
|
|
PVOID pData,
|
|
UINT uiSize
|
|
)
|
|
{
|
|
LPCSTR rv;
|
|
PRCLXDATA pRClxData = NULL;
|
|
CHAR *szName2Send;
|
|
PVOID pData2Send;
|
|
UINT uiPacketSize;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (pCI->dead)
|
|
{
|
|
rv = ERR_CLIENT_IS_DEAD;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!pCI->RClxMode)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "SCSendVCData is not supported in non-RCLXmode\n"));
|
|
rv = ERR_NOTSUPPORTED;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!pData || !uiSize)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "pData and/or uiSize parameter are NULL\n"));
|
|
rv = ERR_INVALID_PARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (strlen(szVCName) > MAX_VCNAME_LEN - 1)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "channel name too long\n"));
|
|
rv = ERR_INVALID_PARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
uiPacketSize = sizeof(*pRClxData) + MAX_VCNAME_LEN + uiSize;
|
|
|
|
pRClxData = malloc(uiPacketSize);
|
|
if (!pRClxData)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "SCSendVCData: can't allocate %d bytes\n",
|
|
uiPacketSize));
|
|
rv = ERR_ALLOCATING_MEMORY;
|
|
goto exitpt;
|
|
}
|
|
|
|
pRClxData->uiType = DATA_VC;
|
|
pRClxData->uiSize = uiPacketSize - sizeof(*pRClxData);
|
|
|
|
szName2Send = (CHAR *)pRClxData->Data;
|
|
strcpy(szName2Send, szVCName);
|
|
|
|
pData2Send = szName2Send + MAX_VCNAME_LEN;
|
|
memcpy(pData2Send, pData, uiSize);
|
|
|
|
rv = _SendRClxData(pCI, pRClxData);
|
|
|
|
exitpt:
|
|
if (pRClxData)
|
|
free(pRClxData);
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* SCRecvVCData
|
|
* Description:
|
|
* Receives data from virtual channel
|
|
* Arguments:
|
|
* pCI - connection context
|
|
* szVCName - the virtual channel name
|
|
* ppData - data pointer
|
|
*
|
|
* !!!!! DON'T FORGET to free() THAT MEMORY !!!!!
|
|
*
|
|
* puiSize - pointer to the data size
|
|
* Return value:
|
|
* error string if fails, NULL on success
|
|
* Called by:
|
|
* * * * EXPORTED * * *
|
|
--*/
|
|
PROTOCOLAPI
|
|
LPCSTR
|
|
SMCAPI
|
|
SCRecvVCData(
|
|
PCONNECTINFO pCI,
|
|
LPCSTR szVCName,
|
|
PVOID pData,
|
|
UINT uiBlockSize,
|
|
UINT *puiBytesRead
|
|
)
|
|
{
|
|
LPCSTR rv;
|
|
LPSTR szRecvVCName;
|
|
PVOID pChanData;
|
|
PRCLXDATACHAIN pIter, pPrev, pNext;
|
|
PRCLXDATACHAIN pRClxDataChain = NULL;
|
|
UINT uiBytesRead = 0;
|
|
BOOL bBlockFree = FALSE;
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Connection info is null\n"));
|
|
rv = ERR_NULL_CONNECTINFO;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (pCI->dead)
|
|
{
|
|
rv = ERR_CLIENT_IS_DEAD;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!pCI->RClxMode)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "SCRecvVCData is not supported in non-RCLXmode\n"));
|
|
rv = ERR_NOTSUPPORTED;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!pData || !uiBlockSize || !puiBytesRead)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Invalid parameters\n"));
|
|
rv = ERR_INVALID_PARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (strlen(szVCName) > MAX_VCNAME_LEN - 1)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "channel name too long\n"));
|
|
rv = ERR_INVALID_PARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
// Extract data entry from this channel
|
|
do {
|
|
if (!pCI->pRClxDataChain)
|
|
{
|
|
rv = _Wait4RClxDataTimeout(pCI, WAIT4STR_TIMEOUT);
|
|
if (rv)
|
|
goto exitpt;
|
|
}
|
|
EnterCriticalSection(g_lpcsGuardWaitQueue);
|
|
|
|
// Search for data from this channel
|
|
{
|
|
pIter = pCI->pRClxDataChain;
|
|
pPrev = NULL;
|
|
|
|
while (pIter)
|
|
{
|
|
pNext = pIter->pNext;
|
|
|
|
if (pIter->RClxData.uiType == DATA_VC &&
|
|
!_stricmp(pIter->RClxData.Data, szVCName))
|
|
{
|
|
|
|
if (pIter->RClxData.uiSize - pIter->uiOffset - MAX_VCNAME_LEN <= uiBlockSize)
|
|
{
|
|
// will read the whole block
|
|
// dispose this entry
|
|
if (pPrev)
|
|
pPrev->pNext = pIter->pNext;
|
|
else
|
|
pCI->pRClxDataChain = pIter->pNext;
|
|
|
|
if (!pIter->pNext)
|
|
pCI->pRClxLastDataChain = pPrev;
|
|
|
|
bBlockFree = TRUE;
|
|
}
|
|
|
|
goto entry_is_found;
|
|
} else
|
|
pPrev = pIter;
|
|
|
|
pIter = pNext;
|
|
}
|
|
entry_is_found:
|
|
|
|
pRClxDataChain = (pIter && pIter->RClxData.uiType == DATA_VC)?
|
|
pIter:NULL;
|
|
}
|
|
LeaveCriticalSection(g_lpcsGuardWaitQueue);
|
|
} while (!pRClxDataChain && !pCI->dead);
|
|
|
|
|
|
ASSERT(pRClxDataChain->RClxData.uiType == DATA_VC);
|
|
|
|
szRecvVCName = pRClxDataChain->RClxData.Data;
|
|
if (_stricmp(szRecvVCName, szVCName))
|
|
{
|
|
TRACE((ERROR_MESSAGE, "SCRecvVCData: received from different channel: %s\n", szRecvVCName));
|
|
ASSERT(0);
|
|
}
|
|
|
|
pChanData = (BYTE *)(pRClxDataChain->RClxData.Data) +
|
|
pRClxDataChain->uiOffset + MAX_VCNAME_LEN;
|
|
uiBytesRead = pRClxDataChain->RClxData.uiSize -
|
|
pRClxDataChain->uiOffset - MAX_VCNAME_LEN;
|
|
if (uiBytesRead > uiBlockSize)
|
|
uiBytesRead = uiBlockSize;
|
|
|
|
|
|
memcpy(pData, pChanData, uiBytesRead);
|
|
|
|
pRClxDataChain->uiOffset += uiBytesRead;
|
|
|
|
rv = NULL;
|
|
|
|
exitpt:
|
|
|
|
if (pRClxDataChain && bBlockFree)
|
|
{
|
|
ASSERT(pRClxDataChain->uiOffset + MAX_VCNAME_LEN == pRClxDataChain->RClxData.uiSize);
|
|
free(pRClxDataChain);
|
|
}
|
|
|
|
if (puiBytesRead)
|
|
{
|
|
*puiBytesRead = uiBytesRead;
|
|
TRACE((INFO_MESSAGE, "SCRecvVCData: %d bytes read\n", uiBytesRead));
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* _EnumWindowsProc
|
|
* Description:
|
|
* Used to find a specific window
|
|
* Arguments:
|
|
* hWnd - current enumerated window handle
|
|
* lParam - pointer to SEARCHWND passed from
|
|
* _FindTopWindow
|
|
* Return value:
|
|
* TRUE on success but window is not found
|
|
* FALSE if the window is found
|
|
* Called by:
|
|
* _FindTopWindow thru EnumWindows
|
|
--*/
|
|
BOOL CALLBACK _EnumWindowsProc( HWND hWnd, LPARAM lParam )
|
|
{
|
|
TCHAR classname[128];
|
|
TCHAR caption[128];
|
|
BOOL rv = TRUE;
|
|
DWORD dwProcessId;
|
|
LONG_PTR lProcessId;
|
|
PSEARCHWND pSearch = (PSEARCHWND)lParam;
|
|
|
|
if (pSearch->szClassName &&
|
|
!GetClassName(hWnd, classname, sizeof(classname)))
|
|
{
|
|
goto exitpt;
|
|
}
|
|
|
|
if (pSearch->szCaption && !GetWindowText(hWnd, caption, sizeof(caption)))
|
|
{
|
|
goto exitpt;
|
|
}
|
|
|
|
GetWindowThreadProcessId(hWnd, &dwProcessId);
|
|
lProcessId = dwProcessId;
|
|
if (
|
|
(!pSearch->szClassName || ! // Check for classname
|
|
#ifdef UNICODE
|
|
wcscmp
|
|
#else
|
|
strcmp
|
|
#endif
|
|
(classname, pSearch->szClassName))
|
|
&&
|
|
(!pSearch->szCaption || !
|
|
#ifdef UNICODE
|
|
wcscmp
|
|
#else
|
|
strcmp
|
|
#endif
|
|
(caption, pSearch->szCaption))
|
|
&&
|
|
lProcessId == pSearch->lProcessId)
|
|
{
|
|
((PSEARCHWND)lParam)->hWnd = hWnd;
|
|
rv = FALSE;
|
|
}
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* _FindTopWindow
|
|
* Description:
|
|
* Find specific window by classname and/or caption and/or process Id
|
|
* Arguments:
|
|
* classname - class name to search for, NULL ignore
|
|
* caption - caption to search for, NULL ignore
|
|
* dwProcessId - process Id, 0 ignore
|
|
* Return value:
|
|
* window handle found, NULL otherwise
|
|
* Called by:
|
|
* SCConnect, SCDisconnect, GetDisconnectResult
|
|
--*/
|
|
HWND _FindTopWindow(LPTSTR classname, LPTSTR caption, LONG_PTR lProcessId)
|
|
{
|
|
SEARCHWND search;
|
|
|
|
search.szClassName = classname;
|
|
search.szCaption = caption;
|
|
search.hWnd = NULL;
|
|
search.lProcessId = lProcessId;
|
|
|
|
EnumWindows(_EnumWindowsProc, (LPARAM)&search);
|
|
|
|
return search.hWnd;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* _FindWindow
|
|
* Description:
|
|
* Find child window by caption and/or classname
|
|
* Arguments:
|
|
* hwndParent - the parent window handle
|
|
* srchcaption - caption to search for, NULL - ignore
|
|
* srchclass - class name to search for, NULL - ignore
|
|
* Return value:
|
|
* window handle found, NULL otherwise
|
|
* Called by:
|
|
* SCConnect
|
|
--*/
|
|
HWND _FindWindow(HWND hwndParent, LPTSTR srchcaption, LPTSTR srchclass)
|
|
{
|
|
HWND hWnd, hwndTop, hwndNext;
|
|
BOOL bFound;
|
|
TCHAR classname[128];
|
|
TCHAR caption[128];
|
|
|
|
hWnd = NULL;
|
|
|
|
hwndTop = GetWindow(hwndParent, GW_CHILD);
|
|
if (!hwndTop)
|
|
{
|
|
TRACE((INFO_MESSAGE, "GetWindow failed. hwnd=0x%x\n", hwndParent));
|
|
goto exiterr;
|
|
}
|
|
|
|
bFound = FALSE;
|
|
hwndNext = hwndTop;
|
|
do {
|
|
hWnd = hwndNext;
|
|
if (srchclass && !GetClassName(hWnd, classname, sizeof(classname)))
|
|
{
|
|
TRACE((INFO_MESSAGE, "GetClassName failed. hwnd=0x%x\n"));
|
|
goto nextwindow;
|
|
}
|
|
if (srchcaption && !GetWindowText(hWnd, caption, sizeof(caption)))
|
|
{
|
|
TRACE((INFO_MESSAGE, "GetWindowText failed. hwnd=0x%x\n"));
|
|
goto nextwindow;
|
|
}
|
|
|
|
if (
|
|
(!srchclass || !
|
|
#ifdef UNICODE
|
|
wcscmp
|
|
#else
|
|
strcmp
|
|
#endif
|
|
(classname, srchclass))
|
|
&&
|
|
(!srchcaption || !
|
|
#ifdef UNICODE
|
|
wcscmp
|
|
#else
|
|
strcmp
|
|
#endif
|
|
(caption, srchcaption))
|
|
)
|
|
bFound = TRUE;
|
|
nextwindow:
|
|
hwndNext = GetNextWindow(hWnd, GW_HWNDNEXT);
|
|
} while (hWnd && hwndNext != hwndTop && !bFound);
|
|
|
|
if (!bFound) goto exiterr;
|
|
|
|
return hWnd;
|
|
exiterr:
|
|
return NULL;
|
|
}
|
|
|
|
BOOL
|
|
_IsExtendedScanCode(INT scancode)
|
|
{
|
|
static BYTE extscans[] = \
|
|
{28, 29, 53, 55, 56, 71, 72, 73, 75, 77, 79, 80, 81, 82, 83, 87, 88};
|
|
INT idx;
|
|
|
|
for (idx = 0; idx < sizeof(extscans); idx++)
|
|
{
|
|
if (scancode == (INT)extscans[idx])
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
PROTOCOLAPI
|
|
BOOL
|
|
SMCAPI
|
|
SCOpenClipboard(HWND hwnd)
|
|
{
|
|
return OpenClipboard(hwnd);
|
|
}
|
|
|
|
PROTOCOLAPI
|
|
BOOL
|
|
SMCAPI
|
|
SCCloseClipboard(VOID)
|
|
{
|
|
return CloseClipboard();
|
|
}
|