Windows2003-3790/termsrv/rdpclip/sclip2.cpp

2007 lines
74 KiB
C++

/**MOD+**********************************************************************/
/* Module: sclip2.cpp */
/* */
/* Purpose: Second thread */
/* Receives RDP clipboard messages */
/* */
/* Copyright(C) Microsoft Corporation 1998 */
/* */
/**MOD-**********************************************************************/
#include <adcg.h>
#define TRC_GROUP TRC_GROUP_CORE
#define TRC_FILE "sclip2"
#include <atrcapi.h>
#include <pclip.h>
#include <sclip.h>
#include <pchannel.h>
#include <shlobj.h>
/****************************************************************************/
/* Global data */
/****************************************************************************/
#include <sclipdat.h>
//
// CBMConvertToClientPath, CBMConvertToClientPathA, CBMConvertToClientPathW
// - Arguments:
// pOldData = Buffer containing the original file path
// pData = Buffer receiving the new file path
// - Returns S_OK if pOldData was a drive path
// E_FAIL if it failed
// - Given a file path with drive letter, this function will strip out the
// colon, and prepend the UNC prefix defined by TS_PREPEND_STRING
//
//
// ***** NOTE *****
// - Currently, if the path is a network path, and not a drive path (C:\path)
// it simply fails
//
HRESULT CBMConvertToClientPath(PVOID pOldData, PVOID pData, size_t cbDest,
BOOL fWide)
{
DC_BEGIN_FN("CBMConvertToClientPath") ;
if (!pOldData)
{
TRC_ERR((TB, _T("Original string pointer is NULL"))) ;
return E_FAIL ;
}
if (!pData)
{
TRC_ERR((TB, _T("Destination string pointer is NULL"))) ;
return E_FAIL ;
}
if (fWide)
return CBMConvertToClientPathW(pOldData, pData, cbDest) ;
else
return CBMConvertToClientPathA(pOldData, pData, cbDest) ;
DC_END_FN() ;
}
HRESULT CBMConvertToClientPathW(PVOID pOldData, PVOID pData, size_t cbDest)
{
wchar_t* filePath ;
wchar_t* driveLetter ;
size_t driveLetterLength ;
HRESULT hr;
DC_BEGIN_FN("CBMConvertToClientPathW") ;
// if this is a filepath with a drive letter, we strip the colon, and
// prefix the path with the temp Directory
filePath = wcschr((wchar_t*)pOldData, L':') ;
if (filePath)
{
hr = StringCbCopyW((wchar_t*)pData, cbDest, CBM.tempDirW);
DC_QUIT_ON_FAIL(hr);
// Now, we start from after the colon in the drive path, and
// find the last '\\' so we have just "\filename"
filePath = wcsrchr(filePath + 1, L'\\');
// Add the leftover "\filename"
if (filePath != NULL) {
hr = StringCbCatW((wchar_t*)pData, cbDest, filePath);
DC_QUIT_ON_FAIL(hr);
}
TRC_DBG((TB,_T("New filename = %s"), (wchar_t*)pData)) ;
}
// else if this is a UNC path beginning with a "\\"
else if (((wchar_t*) pOldData)[0] == L'\\' &&
((wchar_t*) pOldData)[1] == L'\\')
{
// if we receive a path beginning with the TS_PREPEND_STRING then
// we should be smart and convert it back to a path with drive letter
if (0 == _wcsnicmp ((wchar_t *) pOldData,
LTS_PREPEND_STRING, TS_PREPEND_LENGTH))
{
// Skip TS_PREPEND_STRING
driveLetter = ((wchar_t*) pOldData)+TS_PREPEND_LENGTH ;
driveLetterLength = (BYTE*)wcschr(driveLetter, L'\\') -
(BYTE*)driveLetter;
hr = StringCbCopyNW((wchar_t*)pData, cbDest, driveLetter,
driveLetterLength);
DC_QUIT_ON_FAIL(hr);
((wchar_t*)pData)[driveLetterLength] = L'\0' ;
hr = StringCbCatW((wchar_t*)pData, cbDest, L":");
DC_QUIT_ON_FAIL(hr);
filePath = wcschr(driveLetter, L'\\');
if (filePath != NULL) {
hr = StringCbCatW((wchar_t*)pData, cbDest, filePath);
DC_QUIT_ON_FAIL(hr);
}
}
// otherwise, we just got a regular UNC path.
else
{
// Stary by prepending the new file path with the temp directory
hr = StringCbCopyW((wchar_t*) pData, cbDest, CBM.tempDirW) ;
DC_QUIT_ON_FAIL(hr);
// Now, we start from the beginning of the original path,
// find the last '\\' so we have just "\filename"
filePath = wcsrchr((wchar_t*)pOldData, L'\\');
if (filePath != NULL) {
hr = StringCbCatW((wchar_t*) pData, cbDest, filePath) ;
DC_QUIT_ON_FAIL(hr);
}
}
}
else
{
TRC_ERR((TB, _T("Bad path"))) ;
hr = E_FAIL; ;
}
DC_EXIT_POINT:
if (FAILED(hr)) {
TRC_ERR((TB,_T("returning failure; hr=0x%x"), hr));
}
DC_END_FN() ;
return hr ;
}
HRESULT CBMConvertToClientPathA(PVOID pOldData, PVOID pData, size_t cbDest)
{
char* filePath ;
char* driveLetter ;
char* tempPath ;
size_t driveLetterLength ;
HRESULT hr;
DC_BEGIN_FN("CBMConvertToClientPathA") ;
// if this is a filepath with a drive letter, we strip the colon, and
// prefix the path with the temp Directory
filePath = strchr((char*)pOldData, ':') ;
if (filePath)
{
hr = StringCbCopyA( (char*)pData, cbDest, CBM.tempDirA) ;
DC_QUIT_ON_FAIL(hr);
// Now, we start from after the colon in the drive path, and
// find the last '\\' so we have just "\filename"
filePath = strrchr(filePath + 1, '\\');
// Add the leftover "\filename"
if (filePath != NULL) {
hr = StringCbCatA((char*)pData, cbDest, filePath) ;
DC_QUIT_ON_FAIL(hr);
}
}
// else if this is a UNC path beginning with a "\\"
else if (((char*) pOldData)[0] == '\\' &&
((char*) pOldData)[1] == '\\')
{
// if this we receive a path beginning with the TS_PREPEND_STRING then
// we should be smart and convert it back to a path with drive letter
if (0 == _strnicmp ((char*)pOldData,
TS_PREPEND_STRING, TS_PREPEND_LENGTH))
{
// Skip TS_PREPEND_STRING
driveLetter = ((char*) pOldData) + TS_PREPEND_LENGTH ;
driveLetterLength = (BYTE*)strchr(driveLetter, '\\') -
(BYTE*)driveLetter;
hr = StringCbCopyNA((char*)pData, cbDest, driveLetter,
driveLetterLength) ;
DC_QUIT_ON_FAIL(hr);
((char*)pData)[driveLetterLength] = '\0' ;
hr = StringCbCatA((char*)pData, cbDest, ":") ;
DC_QUIT_ON_FAIL(hr);
filePath = strchr(driveLetter, '\\');
if (filePath != NULL) {
hr = StringCbCatA((char*)pData, cbDest, filePath) ;
DC_QUIT_ON_FAIL(hr);
}
}
// otherwise, we just got a regular UNC path.
else
{
// Stary by prepending the new file path with the temp directory
hr = StringCbCopyA((char*) pData, cbDest, CBM.tempDirA) ;
DC_QUIT_ON_FAIL(hr);
// Now, we start from the beginning of the original path,
// find the last '\\' so we have just "\filename"
filePath = strrchr((char*)pOldData, L'\\');
if (filePath != NULL) {
hr = StringCbCatA((char*) pData, cbDest, filePath) ;
DC_QUIT_ON_FAIL(hr);
}
}
}
else
{
TRC_ERR((TB, _T("Bad path"))) ;
hr = E_FAIL ;
}
DC_EXIT_POINT:
if (FAILED(hr)) {
TRC_ERR((TB,_T("returning failure; hr=0x%x"), hr));
}
DC_END_FN() ;
return hr;
}
//
// CBMGetNewFilePathLengthForClient
// - Arguments:
// pData = Buffer containing a filepath
// fWide = Wide or Ansi (TRUE if wide, FALSE if ansi)
// - Returns the size (in bytes) required to convert the path to a client path
// 0 if it fails
//
UINT CBMGetNewFilePathLengthForClient(PVOID pData, BOOL fWide)
{
UINT result ;
DC_BEGIN_FN("CBMGetNewFilePathLengthForClient") ;
if (!pData)
{
TRC_ERR((TB, _T("Filename is NULL"))) ;
result = 0 ;
}
if (fWide)
result = CBMGetNewFilePathLengthForClientW((WCHAR*)pData) ;
else
result = CBMGetNewFilePathLengthForClientA((char*)pData) ;
DC_EXIT_POINT:
DC_END_FN() ;
return result ;
}
UINT CBMGetNewFilePathLengthForClientW(WCHAR* wszOldFilepath)
{
UINT oldLength = wcslen(wszOldFilepath) ;
UINT newLength ;
UINT remainingLength = oldLength ;
byte charSize = sizeof(WCHAR) ;
DC_BEGIN_FN("CBMGetNewFilePathLengthForClientW") ;
// if the old filename didn't even have space for "c:\" (with NULL),
// then its probably invalid
if (4 > oldLength)
{
newLength = 0 ;
DC_QUIT ;
}
// We check to see if the filepath is prefixed by the TS_PREPEND_STRING
// If so, we should be smart, and return the size of it with the prepend
// string removed, and the colon added.
if (0 == _wcsnicmp(wszOldFilepath,
LTS_PREPEND_STRING, TS_PREPEND_LENGTH))
{
newLength = oldLength - TS_PREPEND_LENGTH + 1 ; // +1 is for the colon
DC_QUIT ;
}
while ((0 != remainingLength) && (L'\\' != wszOldFilepath[remainingLength]))
{
remainingLength-- ;
}
// Add the length of the temp directory path, and subtract the
// path preceeding the filename ("path\filename" -> "\filename")
// (\\server\sharename\path\morepath\filename
newLength = oldLength - remainingLength + wcslen(CBM.tempDirW) + 1;
DC_EXIT_POINT:
DC_END_FN() ;
return newLength * charSize ;
}
UINT CBMGetNewFilePathLengthForClientA(char* szOldFilepath)
{
UINT oldLength = strlen(szOldFilepath) ;
UINT newLength ;
UINT remainingLength = oldLength ;
byte charSize = sizeof(char) ;
DC_BEGIN_FN("CBMGetNewFilePathLengthForClientA") ;
// if the old filename didn't even have space for "c:\" (with NULL),
// then its probably invalid
if (4 > oldLength)
{
newLength = 0 ;
DC_QUIT ;
}
// We check to see if the filepath is prefixed by the TS_PREPEND_STRING
// If so, we should be smart, and return the size of it with the prepend
// string removed, and the colon added.
if (0 == _strnicmp(szOldFilepath,
TS_PREPEND_STRING, TS_PREPEND_LENGTH))
{
newLength = oldLength - TS_PREPEND_LENGTH + 1 ; // +1 is for the colon
DC_QUIT ;
}
while ((0 != remainingLength) && ('\\' != szOldFilepath[remainingLength]))
{
remainingLength-- ;
}
// Add the length of the temp directory path, and subtract the
// path preceeding the filename ("path\filename" -> "\filename")
// (\\server\sharename\path\morepath\filename
newLength = oldLength - remainingLength + strlen(CBM.tempDirA) + 1;
DC_EXIT_POINT:
DC_END_FN() ;
return newLength * charSize ;
}
//
// CBMGetNewDropfilesSizeForClientSize
// - Arguments:
// pData = Buffer containing a DROPFILES struct
// oldSize = The size of the DROPFILES struct
// fWide = Wide or Ansi (TRUE if wide, FALSE if ansi)
// - Returns the size required for a conversion of the paths to client paths
// 0 if it fails
//
ULONG CBMGetNewDropfilesSizeForClient(PVOID pData, ULONG oldSize, BOOL fWide)
{
DC_BEGIN_FN("CBMGetNewDropfilesSizeForClientSize") ;
if (fWide)
return CBMGetNewDropfilesSizeForClientW(pData, oldSize) ;
else
return CBMGetNewDropfilesSizeForClientA(pData, oldSize) ;
DC_END_FN() ;
}
ULONG CBMGetNewDropfilesSizeForClientW(PVOID pData, ULONG oldSize)
{
ULONG newSize = oldSize ;
wchar_t* filenameW ;
wchar_t* filePathW ;
wchar_t* fullFilePathW ;
byte charSize ;
DC_BEGIN_FN("CBMGetNewDropfilesSizeForClientSizeW") ;
charSize = sizeof(wchar_t) ;
if (!pData)
{
TRC_ERR((TB,_T("Pointer to dropfile is NULL"))) ;
return 0 ;
}
// The start of the first filename
fullFilePathW = (wchar_t*) ((byte*) pData + ((DROPFILES*) pData)->pFiles) ;
while (L'\0' != fullFilePathW[0])
{
filePathW = wcschr(fullFilePathW, L':') ;
// If the file path has a colon in it, then it's a valid drive path
if (filePathW)
{
// we add space for (strlen(tempDir)-1+1) characters because
// although we are adding strlen(tempDir) characters, we are
// stripping out the colon from the filepath; however, we add
// an extra "\" to the string because the tempDir does not have
// a trailing "\"
filenameW = wcsrchr(filePathW, L'\\');
// Add the length of the temp directory path, and subtract the
// path preceeding the filename ("path\filename" -> "\filename")
// (\\server\sharename\path\morepath\filename
newSize += (wcslen(CBM.tempDirW) + (filenameW - fullFilePathW))
* charSize ;
}
// Otherwise, it is a UNC path
else if (fullFilePathW[0] == L'\\' &&
fullFilePathW[1] == L'\\')
{
// if we receive a path beginning with the TS_PREPEND_STRING then
// we should be smart and convert it back to a path with drive letter
if (0 == _wcsnicmp(fullFilePathW,
LTS_PREPEND_STRING, TS_PREPEND_LENGTH))
{
newSize = newSize - (TS_PREPEND_LENGTH - 1) * charSize ;
}
else
{
filenameW = wcsrchr(fullFilePathW, L'\\');
// Add the length of the temp directory path, and subtract the
// path preceeding the filename ("path\filename" -> "\filename")
// (\\server\sharename\path\morepath\filename
newSize += (wcslen(CBM.tempDirW) - (filenameW - fullFilePathW))
* charSize ;
}
}
else
{
TRC_ERR((TB,_T("Bad path"))) ;
return 0 ;
}
fullFilePathW = fullFilePathW + (wcslen(fullFilePathW) + 1) ;
}
// Add space for extra null character
newSize += charSize ;
DC_END_FN() ;
return newSize ;
}
ULONG CBMGetNewDropfilesSizeForClientA(PVOID pData, ULONG oldSize)
{
ULONG newSize = oldSize ;
char* filename ;
char* filePath ;
char* fullFilePath ;
byte charSize ;
DC_BEGIN_FN("CBMGetNewDropfilesSizeForClientSizeW") ;
charSize = sizeof(char) ;
if (!pData)
{
TRC_ERR((TB,_T("Pointer to dropfile is NULL"))) ;
return 0 ;
}
// The start of the first filename
fullFilePath = (char*) ((byte*) pData + ((DROPFILES*) pData)->pFiles) ;
while ('\0' != fullFilePath[0])
{
filePath = strchr(fullFilePath, ':') ;
// If the file path has a colon in it, then its a valid drive path
if (filePath)
{
// we add space for (strlen(tempDir)-1+1) characters because
// although we are adding strlen(tempDir) characters, we are
// stripping out the colon from the filepath; however, we add
// an extra "\" to the string because the tempDir does not have
// a trailing "\"
filename = strrchr(filePath, '\\');
// Add the length of the temp directory path, and subtract the
// path preceeding the filename ("path\filename" -> "\filename")
// (\\server\sharename\path\morepath\filename
newSize += (strlen(CBM.tempDirA) + (filename - fullFilePath))
* charSize ;
}
// Otherwise, it is a UNC path
else if (fullFilePath[0] == '\\' &&
fullFilePath[1] == '\\')
{
// if we receive a path beginning with the TS_PREPEND_STRING then
// we should be smart and convert it back to a path with drive letter
if (0 == _strnicmp(fullFilePath,
TS_PREPEND_STRING, TS_PREPEND_LENGTH))
{
newSize = newSize - (TS_PREPEND_LENGTH - 1) * charSize ;
}
else
{
filename = strrchr(fullFilePath, '\\');
// Add the length of the temp directory path, and subtract the
// path preceeding the filename ("path\filename" -> "\filename")
// (\\server\sharename\path\morepath\filename
newSize += (strlen(CBM.tempDirA) + (filename - fullFilePath))
* charSize ;
}
}
else
{
TRC_ERR((TB,_T("Bad path"))) ;
return 0 ;
}
fullFilePath = fullFilePath + (strlen(fullFilePath) + 1) ;
}
// Add space for extra null character
newSize += charSize ;
DC_END_FN() ;
return newSize ;
}
//
// ClipConvertToTempPath, ClipConvertToTempPathA, ClipConvertToTempPathW
// - Arguments:
// pSrcFiles = buffer containing the names/path of the files to be copied
// - Returns 0 if successful
// nonzero if failed
// - Given a list of file names/paths, this function will attempt to copy them
// to the temp directory
//
int CBMCopyToTempDirectory(PVOID pSrcFiles, BOOL fWide)
{
int result ;
if (fWide)
result = CBMCopyToTempDirectoryW(pSrcFiles) ;
else
result = CBMCopyToTempDirectoryA(pSrcFiles) ;
return result ;
}
int CBMCopyToTempDirectoryW(PVOID pSrcFiles)
{
DC_BEGIN_FN("CBMCopyToTempDirectoryW") ;
SHFILEOPSTRUCTW fileOpStructW ;
int result ;
HRESULT hr;
// these are the temp, "temp directories"; they are used because we cannot
// directly perform a conversion to client path CBM.tempDir*, because they
// are used within the conversion routines!
wchar_t tempDirW[MAX_PATH] ;
hr = CBMConvertToServerPath(CBM.tempDirW, tempDirW, sizeof(tempDirW), 1) ;
if (FAILED(hr)) {
TRC_ERR((TB,_T("CBMConvertToServerPath failed hr=0x%x"), hr));
result = hr;
DC_QUIT;
}
fileOpStructW.pFrom = (WCHAR*) pSrcFiles ;
fileOpStructW.pTo = tempDirW ;
fileOpStructW.hwnd = NULL ;
fileOpStructW.wFunc = CBM.dropEffect ;
fileOpStructW.fFlags = FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR |
FOF_SIMPLEPROGRESS | FOF_ALLOWUNDO ;
fileOpStructW.hNameMappings = NULL ;
fileOpStructW.lpszProgressTitle = CBM.szPasteInfoStringW;
result = SHFileOperationW(&fileOpStructW) ;
DC_EXIT_POINT:
DC_END_FN();
return result ;
}
int CBMCopyToTempDirectoryA(PVOID pSrcFiles)
{
DC_BEGIN_FN("CBMCopyToTempDirectoryA") ;
SHFILEOPSTRUCTA fileOpStructA ;
int result ;
HRESULT hr;
char tempDirA[MAX_PATH] ;
hr = CBMConvertToServerPath(CBM.tempDirA, tempDirA, sizeof(tempDirA), 0) ;
if (FAILED(hr)) {
TRC_ERR((TB,_T("CBMConvertToServerPath failed hr=0x%x"), hr));
result = hr;
DC_QUIT;
}
fileOpStructA.pFrom = (char*) pSrcFiles ;
fileOpStructA.pTo = tempDirA ;
fileOpStructA.hwnd = NULL ;
fileOpStructA.wFunc = CBM.dropEffect ;
fileOpStructA.fFlags = FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR |
FOF_SIMPLEPROGRESS | FOF_ALLOWUNDO ;
fileOpStructA.hNameMappings = NULL ;
fileOpStructA.lpszProgressTitle = CBM.szPasteInfoStringA;
result = SHFileOperationA(&fileOpStructA) ;
DC_EXIT_POINT:
DC_END_FN();
return result ;
}
/****************************************************************************/
/* CBMOnDataReceived - handle incoming data */
/****************************************************************************/
DCVOID DCINTERNAL CBMOnDataReceived(PDCUINT8 pBuffer, DCUINT cbBytes)
{
PCHANNEL_PDU_HEADER pHdr;
PDCUINT8 pData;
DCUINT copyLen;
DCBOOL freeTheBuffer = FALSE;
PTS_CLIP_PDU pClipPDU;
DC_BEGIN_FN("CBMOnDataReceived");
SetEvent(CBM.GetDataSync[TS_BLOCK_RECEIVED]) ;
pHdr = (PCHANNEL_PDU_HEADER)pBuffer;
pData = (PDCUINT8)(pHdr + 1);
TRC_DBG((TB, _T("Header at %p: flags %#x, length %d"),
pHdr, pHdr->flags, pHdr->length));
// Check to be sure we have at least a header worth of data
if (sizeof(CHANNEL_PDU_HEADER) > cbBytes) {
TRC_ERR((TB,_T("Packet not large enough to contain header; cbBytes=%u"),
cbBytes));
freeTheBuffer = TRUE;
DC_QUIT;
}
/************************************************************************/
/* First chunk - allocate memory to hold the entire message */
/************************************************************************/
if (pHdr->flags & CHANNEL_FLAG_FIRST)
{
TRC_NRM((TB, _T("First chunk - %d of %d"), cbBytes, pHdr->length));
CBM.rxpBuffer = (PDCUINT8) LocalAlloc(LMEM_FIXED, pHdr->length);
if (CBM.rxpBuffer)
{
CBM.rxpNext = CBM.rxpBuffer;
CBM.rxSize = pHdr->length;
CBM.rxLeft = pHdr->length;
}
else
{
TRC_ERR((TB, _T("Failed to alloc %d bytes for rx buffer"),
pHdr->length));
DC_QUIT;
}
}
/************************************************************************/
/* Check that we have a buffer available */
/************************************************************************/
if (!CBM.rxpBuffer)
{
TRC_ERR((TB, _T("No rx buffer")));
DC_QUIT;
}
/************************************************************************/
/* Check there's enough space left */
/************************************************************************/
copyLen = cbBytes - sizeof(*pHdr);
if (CBM.rxLeft < copyLen)
{
TRC_ERR((TB, _T("Not enough space in rx buffer: need/got %d/%d"),
copyLen, CBM.rxLeft));
freeTheBuffer = TRUE;
DC_QUIT;
}
/************************************************************************/
/* Copy the data */
/************************************************************************/
TRC_DBG((TB, _T("Copy %d bytes to %p"), copyLen, CBM.rxpNext));
DC_MEMCPY(CBM.rxpNext, pData, copyLen);
CBM.rxpNext += copyLen;
CBM.rxLeft -= copyLen;
/************************************************************************/
/* If we have a complete buffer, tell the main thread */
/************************************************************************/
if (pHdr->flags & CHANNEL_FLAG_LAST)
{
/********************************************************************/
/* Check that we received all the data */
/********************************************************************/
if (CBM.rxLeft != 0)
{
TRC_ERR((TB, _T("Didn't receive all the data: expect/got: %d/%d"),
CBM.rxSize, CBM.rxSize - CBM.rxLeft));
freeTheBuffer = TRUE;
DC_QUIT;
}
// Check that we received at least a TS_CLIP_PDU in length
if (FIELDOFFSET(TS_CLIP_PDU, data) > CBM.rxSize) {
TRC_ERR((TB,_T("Assembled buffer to short for TS_CLIP_PDU ")
_T(" [need=%u got=%u]"), FIELDOFFSET(TS_CLIP_PDU, data),
CBM.rxSize));
freeTheBuffer = TRUE;
DC_QUIT;
}
/********************************************************************/
// If this message contains a response to our format list, a request
// for clipboard data or the clipboard data that we requested
// handle it in this thread (it will not block us for long)
// Otherwise,
// Tell the main thread. The main thread will free the buffer when
// it's done with this message.
/********************************************************************/
pClipPDU = (PTS_CLIP_PDU) CBM.rxpBuffer ;
// Validate that there is enough data to read whatever is advertised
// in the pClipPDU->dataLen
if (pClipPDU->dataLen + FIELDOFFSET(TS_CLIP_PDU, data) > CBM.rxSize) {
TRC_ERR((TB,_T("TS_CLIP_PDU.dataLen field too large")));
freeTheBuffer = TRUE;
DC_QUIT;
}
switch (pClipPDU->msgType)
{
case TS_CB_FORMAT_LIST_RESPONSE:
{
TRC_NRM((TB, _T("TS_CB_FORMAT_LIST_RESPONSE received")));
CBMOnFormatListResponse(pClipPDU);
LocalFree(pClipPDU);
}
break;
case TS_CB_FORMAT_DATA_REQUEST:
{
TRC_NRM((TB, _T("TS_CB_FORMAT_DATA_REQUEST received")));
CBMOnFormatDataRequest(pClipPDU);
LocalFree(pClipPDU);
}
break;
case TS_CB_FORMAT_DATA_RESPONSE:
{
TRC_NRM((TB, _T("TS_CB_FORMAT_DATA_RESPONSE received")));
CBMOnFormatDataResponse(pClipPDU);
LocalFree(pClipPDU);
}
break;
case TS_CB_TEMP_DIRECTORY:
{
TRC_NRM((TB, _T("TS_CB_TEMP_DIRECTORY received")));
CBM.fFileCutCopyOn = CBMOnReceivedTempDirectory(pClipPDU);
LocalFree(pClipPDU);
}
break;
default:
{
// Free the Clipboard thread, if locked
if (TS_CB_FORMAT_LIST == pClipPDU->msgType)
{
SetEvent(CBM.GetDataSync[TS_RESET_EVENT]) ;
TRC_NRM((TB,_T("Reset state; free clipboard if locked"))) ;
}
TRC_NRM((TB, _T("Pass %d bytes to main thread"), CBM.rxSize));
PostMessage(CBM.viewerWindow,
CBM.regMsg,
CBM.rxSize,
(LPARAM)CBM.rxpBuffer);
}
break;
}
}
DC_EXIT_POINT:
/************************************************************************/
/* Free the buffer if necessary */
/************************************************************************/
if (freeTheBuffer && CBM.rxpBuffer)
{
TRC_DBG((TB, _T("Free rx buffer")));
LocalFree(CBM.rxpBuffer);
CBM.rxpBuffer = NULL;
}
DC_END_FN();
return;
} /* CBMOnDataReceived */
/****************************************************************************/
/* the second thread procedure */
/****************************************************************************/
DWORD WINAPI CBMDataThreadProc( LPVOID pParam )
{
DWORD waitRc = 0;
BOOL fSuccess;
DWORD dwResult;
DCUINT8 readBuffer[CHANNEL_PDU_LENGTH];
DWORD cbBytes = 0;
DCBOOL dataRead;
DCBOOL tryToDoRead;
DC_BEGIN_FN("CBMDataThreadProc");
DC_IGNORE_PARAMETER(pParam);
/************************************************************************/
/* loop until we're told to stop */
/************************************************************************/
while (CBM.runThread)
{
dataRead = FALSE;
tryToDoRead = (CBM.vcHandle != NULL) ? TRUE : FALSE;
if (tryToDoRead)
{
/****************************************************************/
/* Issue a read */
/****************************************************************/
cbBytes = sizeof(readBuffer);
TRC_DBG((TB, _T("Issue the read")));
fSuccess = ReadFile(CBM.vcHandle,
readBuffer,
sizeof(readBuffer),
&cbBytes,
&CBM.readOL);
if (fSuccess)
{
TRC_NRM((TB, _T("Data read instantly")));
dataRead = TRUE;
}
else
{
dwResult = GetLastError();
TRC_DBG((TB, _T("Read failed, %d"), dwResult));
if (dwResult != ERROR_IO_PENDING)
{
/********************************************************/
/* The read failed. Treat this like a disconnection - */
/* stick around and wait to be reconnected. */
/********************************************************/
TRC_ERR((TB, _T("Read failed, %d"), dwResult));
tryToDoRead = FALSE;
}
}
}
/********************************************************************/
/* If we haven't read any data, wait for something to happen now */
/********************************************************************/
if (!dataRead)
{
waitRc = WaitForMultipleObjects(CLIP_EVENT_COUNT,
CBM.hEvent,
FALSE,
INFINITE);
switch (waitRc)
{
/************************************************************/
/* Handle Disconnect and Reconnect synchronously, so that */
/* all state changes are complete on return. */
/************************************************************/
case WAIT_OBJECT_0 + CLIP_EVENT_DISCONNECT:
{
TRC_NRM((TB, _T("Session disconnected")));
// Make sure that if the other rdpclip thread is waiting
// for a response in GetData() it is notified of the
// disconnection.
if (CBM.GetDataSync[TS_DISCONNECT_EVENT]) {
SetEvent(CBM.GetDataSync[TS_DISCONNECT_EVENT]);
}
ResetEvent(CBM.hEvent[CLIP_EVENT_DISCONNECT]);
SendMessage(CBM.viewerWindow,
CBM.regMsg,
0,
CLIP_EVENT_DISCONNECT);
tryToDoRead = FALSE;
}
break;
case WAIT_OBJECT_0 + CLIP_EVENT_RECONNECT:
{
TRC_NRM((TB, _T("Session reconnected")));
ResetEvent(CBM.hEvent[CLIP_EVENT_RECONNECT]);
SendMessage(CBM.viewerWindow,
CBM.regMsg,
0,
CLIP_EVENT_RECONNECT);
tryToDoRead = TRUE;
}
break;
/************************************************************/
/* Data received */
/************************************************************/
case WAIT_OBJECT_0 + CLIP_EVENT_READ:
{
TRC_DBG((TB, _T("Read complete")));
fSuccess = GetOverlappedResult(CBM.vcHandle,
&CBM.readOL,
&cbBytes,
FALSE);
if (fSuccess)
{
dataRead = TRUE;
}
else
{
dwResult = GetLastError();
TRC_ERR((TB, _T("GetOverlappedResult failed %d"),
dwResult));
tryToDoRead = FALSE;
}
/********************************************************/
/* Reset the event, otherwise we can come straight back */
/* in here if we don't retry the read. */
/********************************************************/
ResetEvent(CBM.hEvent[CLIP_EVENT_READ]);
}
break;
/************************************************************/
/* Error occurred - treat as disconnect */
/************************************************************/
case WAIT_FAILED:
default:
{
dwResult = GetLastError();
TRC_ERR((TB, _T("Wait failed, result %d"), dwResult));
tryToDoRead = FALSE;
}
break;
}
}
/********************************************************************/
/* Once we get here, the read is complete - see what we got */
/********************************************************************/
if (dataRead && CBM.runThread)
{
TRC_NRM((TB, _T("%d bytes of data received"), cbBytes));
TRC_DATA_DBG("Data received", readBuffer, cbBytes);
CBMOnDataReceived(readBuffer, cbBytes);
}
} /* while */
TRC_NRM((TB, _T("Thread ending")));
DC_EXIT_POINT:
DC_END_FN();
ExitThread(waitRc);
return(waitRc);
}
/****************************************************************************/
/* CBMOnReceivedTempDirectory */
/* Caller must have validated that the PDU contained enough data for the */
/* length specified in pClipPDU->dataLen */
/****************************************************************************/
DCBOOL DCINTERNAL CBMOnReceivedTempDirectory(PTS_CLIP_PDU pClipPDU)
{
DCBOOL fSuccess = FALSE ;
WCHAR tempDirW[MAX_PATH] ;
UINT pathLength = pClipPDU->dataLen / sizeof(WCHAR) ;
HRESULT hr;
wchar_t *pDummy;
size_t cbDummy;
DC_BEGIN_FN("CBMOnReceivedTempDirectory");
if (pathLength > MAX_PATH)
{
TRC_ERR((TB, _T("Path too big for us. Failing"))) ;
fSuccess = FALSE ;
DC_QUIT ;
}
if (sizeof(WCHAR) > pClipPDU->dataLen) {
TRC_ERR((TB,_T("Not enough data to read anything from buffer")));
fSuccess = FALSE;
DC_QUIT;
}
// The incoming data is not necessarily NULL terminated
hr = StringCbCopyNExW(tempDirW, sizeof(tempDirW), (WCHAR*) pClipPDU->data,
pClipPDU->dataLen, &pDummy, &cbDummy, 0 );
if (FAILED(hr)) {
fSuccess = FALSE;
DC_QUIT;
}
// Check that the string is NULL terminated
hr = StringCbLengthW(tempDirW, sizeof(tempDirW), &cbDummy);
if (FAILED(hr)) {
fSuccess = FALSE;
DC_QUIT;
}
hr = CBMConvertToServerPath(tempDirW, CBM.baseTempDirW,
sizeof(CBM.baseTempDirW), 1) ;
if (FAILED(hr)) {
TRC_ERR((TB,_T("CBMConvertToServerPath failed hr=0x%x"), hr ));
fSuccess = FALSE;
DC_QUIT;
}
fSuccess = TRUE ;
DC_EXIT_POINT:
DC_END_FN() ;
return fSuccess ;
}
/****************************************************************************/
/* CBMOnFormatListResponse */
/* Caller must have validated that the PDU contained enough data for the */
/* length specified in pClipPDU->dataLen */
/****************************************************************************/
DCVOID DCINTERNAL CBMOnFormatListResponse(PTS_CLIP_PDU pClipPDU)
{
DC_BEGIN_FN("CBMOnFormatListResponse");
/************************************************************************/
/* The client has received our format list */
/************************************************************************/
TRC_NRM((TB, _T("Received FORMAT_LIST_REPSONSE")));
CBM_CHECK_STATE(CBM_EVENT_FORMAT_LIST_RSP);
/************************************************************************/
/* This may arrive just after we've sent the client a format list - */
/* since the client always wins, we must accept the list */
/************************************************************************/
if (CBM.state != CBM_STATE_PENDING_FORMAT_LIST_RSP)
{
TRC_ALT((TB, _T("Got unexpected list response")));
CBM.formatResponseCount = 0;
}
else
{
/********************************************************************/
/* update our state according to the result */
/********************************************************************/
CBM.formatResponseCount--;
TRC_NRM((TB, _T("Waiting for %d format response(s)"),
CBM.formatResponseCount));
if (CBM.formatResponseCount <= 0)
{
if (pClipPDU->msgFlags == TS_CB_RESPONSE_OK)
{
TRC_DBG((TB, _T("Fmt list response OK")));
CBM_SET_STATE(CBM_STATE_SHARED_CB_OWNER, CBM_EVENT_FORMAT_LIST_RSP);
}
else
{
TRC_ALT((TB, _T("Fmt list rsp failed")));
CBM_SET_STATE(CBM_STATE_CONNECTED, CBM_EVENT_FORMAT_LIST_RSP);
}
CBM.formatResponseCount = 0;
}
/********************************************************************/
/* close the local CB - if it's open - and tell the next viewer */
/* about the updated list */
/********************************************************************/
if (CBM.open)
{
TRC_NRM((TB, _T("Close clipboard - didn't expect that")));
if (!CloseClipboard())
{
TRC_SYSTEM_ERROR("CloseClipboard");
}
CBM.open = FALSE;
}
if (CBM.nextViewer != NULL)
{
PostMessage(CBM.nextViewer, WM_DRAWCLIPBOARD,0,0);
}
}
DC_EXIT_POINT:
DC_END_FN();
return;
} /* CBMOnFormatListResponse */
//
// CBMOnFormatDataRequest
// - Sends client format data it requested
/* Caller must have validated that the PDU contained enough data for the */
/* length specified in pClipPDU->dataLen */
//
DCVOID DCINTERNAL CBMOnFormatDataRequest(PTS_CLIP_PDU pClipPDU)
{
DCUINT16 response = TS_CB_RESPONSE_OK;
HANDLE hData = NULL;
PDCVOID pData;
PDCVOID pNewData;
HANDLE hNewData = NULL;
HANDLE hDropData = NULL;
HANDLE hTempData = NULL;
DCINT32 numEntries;
DCUINT32 dataLen = 0;
DCUINT32 pduLen;
DCUINT formatID;
LOGPALETTE * pLogPalette = NULL;
PTS_CLIP_PDU pClipRsp;
TS_CLIP_PDU clipRsp;
DCBOOL fSuccess = TRUE ;
BOOL fWide ;
byte charSize ;
DROPFILES* pDropFiles ;
BOOL fDrivePath ;
ULONG newSize, oldSize ;
HPDCVOID pFileList ;
HPDCVOID pFilename ;
HPDCVOID pOldFilename ;
SHFILEOPSTRUCTA fileOpStructA ;
SHFILEOPSTRUCTW fileOpStructW ;
wchar_t tempDirW[MAX_PATH] ;
char tempDir[MAX_PATH] ;
DCTCHAR formatName[TS_FORMAT_NAME_LEN] ;
DC_BEGIN_FN("CBMOnFormatDataRequest");
// The client wants a format from us
TRC_NRM((TB, _T("Received FORMAT_DATA_REQUEST")));
// This may arrive just after we've sent the client a format list -
// since the client has not confirmed our list, this request is out-of-
// date. Fail it.
if (CBMCheckState(CBM_EVENT_FORMAT_DATA_RQ) != CBM_TABLE_OK)
{
TRC_ALT((TB, _T("Unexpected format data rq")));
// close the local CB - if it's open - and tell the next viewer
// about the updated list
if (CBM.open)
{
TRC_NRM((TB, _T("Close clipboard")));
if (!CloseClipboard())
{
TRC_SYSTEM_ERROR("CloseClipboard");
}
CBM.open = FALSE;
}
if (CBM.nextViewer != NULL)
{
PostMessage(CBM.nextViewer, WM_DRAWCLIPBOARD,0,0);
}
//
// Fail the data request
//
response = TS_CB_RESPONSE_FAIL;
goto CB_SEND_RESPONSE;
}
if (sizeof(DCUINT) > pClipPDU->dataLen) {
TRC_ERR((TB,_T("Not enough data to read format [need=%u got=%u]"),
sizeof(DCUINT), pClipPDU->dataLen ));
response = TS_CB_RESPONSE_FAIL;
goto CB_SEND_RESPONSE;
}
formatID = *((PDCUINT)(pClipPDU->data));
TRC_NRM((TB, _T("format ID %d"), formatID));
/************************************************************************/
/* Open the local clipboard */
/************************************************************************/
if (!CBM.open)
{
if (!OpenClipboard(CBM.viewerWindow))
{
TRC_SYSTEM_ERROR("OpenCB");
response = TS_CB_RESPONSE_FAIL;
goto CB_SEND_RESPONSE;
}
}
/************************************************************************/
/* It was/is open */
/************************************************************************/
TRC_NRM((TB, _T("CB opened")));
CBM.open = TRUE;
/************************************************************************/
/* Get a handle to the data */
/************************************************************************/
hData = GetClipboardData(formatID);
if (hData == NULL)
{
/********************************************************************/
/* Oops! */
/********************************************************************/
TRC_ERR((TB, _T("Failed to get format %d"),formatID));
response = TS_CB_RESPONSE_FAIL;
dataLen = 0;
goto CB_SEND_RESPONSE;
}
/************************************************************************/
/* Got handle, now what happens next depends on the flavour of data */
/* we're looking at... */
/************************************************************************/
if (formatID == CF_PALETTE)
{
DCUINT16 entries;
TRC_DBG((TB, _T("CF_PALETTE requested")));
/********************************************************************/
/* Find out how many entries there are in the palette and allocate */
/* enough memory to hold them all. */
/********************************************************************/
if (GetObject(hData, sizeof(DCUINT16), &entries) == 0)
{
TRC_DBG((TB, _T("Failed count of palette entries")));
entries = 256;
}
numEntries = entries;
TRC_DBG((TB, _T("Need mem for %d palette entries"), numEntries));
dataLen = sizeof(LOGPALETTE) +
((numEntries - 1) * sizeof(PALETTEENTRY));
hNewData = GlobalAlloc(GHND, dataLen);
if (hNewData == 0)
{
TRC_ERR((TB, _T("Failed to get %d bytes for palette"), dataLen));
response = TS_CB_RESPONSE_FAIL;
dataLen = 0;
}
else
{
hDropData = hNewData;
/****************************************************************/
/* now get the palette entries into the new buffer */
/****************************************************************/
pData = GlobalLock(hNewData);
numEntries = GetPaletteEntries((HPALETTE)hData,
0,
numEntries,
(PALETTEENTRY*)pData);
GlobalUnlock(hNewData);
TRC_DBG((TB, _T("Got %d pal entries"), numEntries));
if (numEntries == 0)
{
TRC_ERR((TB, _T("Failed to get any pal entries")));
response = TS_CB_RESPONSE_FAIL;
dataLen = 0;
}
dataLen = numEntries * sizeof(PALETTEENTRY);
/****************************************************************/
/* all ok - set up hData to point to the new data */
/****************************************************************/
//GlobalFree(hData);
hData = hNewData;
}
}
else if (formatID == CF_METAFILEPICT)
{
TRC_NRM((TB, _T("Metafile data to get")));
/********************************************************************/
/* Metafiles are copied as Handles - we need to send across the */
/* actual bits */
/********************************************************************/
hNewData = CBMGetMFData(hData, &dataLen);
if (!hNewData)
{
TRC_ERR((TB, _T("Failed to set MF data")));
response = TS_CB_RESPONSE_FAIL;
dataLen = 0;
}
else
{
hDropData = hNewData;
/****************************************************************/
/* all ok - set up hData to point to the new data */
/****************************************************************/
hData = hNewData;
}
}
else if (formatID == CF_HDROP)
{
TRC_NRM((TB,_T("HDROP requested"))) ;
pDropFiles = (DROPFILES*) GlobalLock(hData) ;
fWide = pDropFiles->fWide ;
charSize = fWide ? sizeof(wchar_t) : sizeof(char) ;
if (!CBM.fAlreadyCopied)
{
// if its not a drive path, then copy to a temp directory
pFileList = (byte*) pDropFiles + pDropFiles->pFiles ;
// CBMCopyToTempDirectory returns 0 if successful
if (0 != CBMCopyToTempDirectory(pFileList, fWide))
{
TRC_ERR((TB,_T("Copy to tmp directory failed"))) ;
response = TS_CB_RESPONSE_FAIL;
dataLen = 0;
CBM.fAlreadyCopied = TRUE ;
goto CB_SEND_RESPONSE;
}
CBM.fAlreadyCopied = TRUE ;
}
// Now that we copied the files, we want to convert the file
// paths to something the client will understand
// Allocate space for new filepaths
oldSize = (ULONG) GlobalSize(hData) ;
newSize = CBMGetNewDropfilesSizeForClient(pDropFiles, oldSize, fWide) ;
hNewData = GlobalAlloc(GMEM_DISCARDABLE | GMEM_MOVEABLE, newSize) ;
if (hNewData == NULL)
{
TRC_ERR((TB, _T("Failed to get %ld bytes for HDROP"),
newSize));
response = TS_CB_RESPONSE_FAIL;
dataLen = 0;
goto CB_SEND_RESPONSE;
}
hDropData = hNewData;
pNewData = GlobalLock(hNewData) ;
if (pNewData == NULL)
{
TRC_ERR((TB, _T("Failed to get lock %p for HDROP"),
hNewData));
response = TS_CB_RESPONSE_FAIL;
dataLen = 0;
goto CB_SEND_RESPONSE;
}
// Just copy the old DROPFILES data members (unchanged)
((DROPFILES*) pNewData)->pFiles = pDropFiles->pFiles ;
((DROPFILES*) pNewData)->pt = pDropFiles->pt ;
((DROPFILES*) pNewData)->fNC = pDropFiles->fNC ;
((DROPFILES*) pNewData)->fWide = pDropFiles->fWide ;
// The first filename in a DROPFILES data structure begins
// DROPFILES.pFiles bytes away from the head of the DROPFILES
pOldFilename = (byte*) pDropFiles + pDropFiles->pFiles ;
pFilename = (byte*) pNewData + ((DROPFILES*) pNewData)->pFiles ;
while (fWide ? (L'\0' != ((wchar_t*) pOldFilename)[0]) : ('\0' != ((char*) pOldFilename)[0]))
{
if ((ULONG)((BYTE*)pFilename-(BYTE*)pNewData) > newSize) {
TRC_ERR((TB,_T("Failed filename conversion, not enough data")));
}
else {
if (!SUCCEEDED(CBMConvertToClientPath(pOldFilename, pFilename,
newSize - ((BYTE*)pFilename-(BYTE*)pNewData), fWide)))
{
TRC_ERR((TB, _T("Failed conversion"))) ;
}
else
{
if (fWide)
{
TRC_NRM((TB,_T("oldname %ls; newname %ls"), (wchar_t*)pOldFilename, (wchar_t*)pFilename)) ;
}
else
{
TRC_NRM((TB,_T("oldname %hs; newname %hs"), (char*)pOldFilename, (char*)pFilename)) ;
}
}
}
if (fWide)
{
pOldFilename = (byte*) pOldFilename + (wcslen((wchar_t*)pOldFilename) + 1) * sizeof(wchar_t) ;
pFilename = (byte*) pFilename + (wcslen((wchar_t*)pFilename) + 1) * sizeof(wchar_t) ;
}
else
{
pOldFilename = (byte*) pOldFilename + (strlen((char*)pOldFilename) + 1) * sizeof(char) ;
pFilename = (byte*) pFilename + (strlen((char*)pFilename) + 1) * sizeof(char) ;
}
}
if (fWide)
((wchar_t*)pFilename)[0] = L'\0' ;
else
((char*)pFilename)[0] = '\0' ;
GlobalUnlock(hNewData) ;
hData = hNewData ;
dataLen = (DWORD) GlobalSize(hData) ;
}
else
{
// Check to see if we are processing the FileName/FileNameW
// OLE 1 formats; if so, we convert the filenames
if (0 != GetClipboardFormatName(formatID, formatName, TS_FORMAT_NAME_LEN))
{
if ((0 == _tcscmp(formatName, TEXT("FileName"))) ||
(0 == _tcscmp(formatName, TEXT("FileNameW"))))
{
if (0 == _tcscmp(formatName, TEXT("FileNameW")))
{
fWide = TRUE ;
charSize = sizeof(WCHAR) ;
}
else
{
fWide = FALSE ;
charSize = 1 ;
}
pOldFilename = GlobalLock(hData) ;
if (!pOldFilename)
{
TRC_ERR((TB, _T("No filename/Unable to lock %p"),
hData));
response = TS_CB_RESPONSE_FAIL;
dataLen = 0;
goto CB_SEND_RESPONSE;
}
oldSize = (ULONG)GlobalSize(hData) ;
if (!CBM.fAlreadyCopied)
{
// if its not a drive path, then copy to a temp
// directory. We have to copy over the filename to
// string that is one character larger, because we
// need to add an extra NULL for the SHFileOperation
pFileList = (char*) LocalAlloc(LPTR, oldSize + charSize) ;
if (fWide)
{
wcscpy((WCHAR*)pFileList, (WCHAR*)pOldFilename) ;
fDrivePath = (0 != wcschr((WCHAR*) pFileList, L':')) ;
}
else
{
strcpy((char*)pFileList, (char*)pOldFilename) ;
fDrivePath = (0 != strchr((char*) pFileList, ':')) ;
}
// CBMCopyToTempDirectory returns 0 if successful
if (0 != CBMCopyToTempDirectory(pFileList, fWide))
{
TRC_ERR((TB,_T("Copy to tmp directory failed"))) ;
response = TS_CB_RESPONSE_FAIL;
dataLen = 0;
CBM.fAlreadyCopied = TRUE ;
goto CB_SEND_RESPONSE;
}
CBM.fAlreadyCopied = TRUE ;
}
newSize = CBMGetNewFilePathLengthForClient(pOldFilename, fWide) ;
hNewData = GlobalAlloc(GMEM_DISCARDABLE | GMEM_MOVEABLE, newSize) ;
if (!hNewData)
{
TRC_ERR((TB, _T("Failed to get %ld bytes for FileName(W)"),
newSize));
response = TS_CB_RESPONSE_FAIL;
dataLen = 0;
goto CB_SEND_RESPONSE;
}
hDropData = hNewData;
pFilename= GlobalLock(hNewData) ;
if (!pFilename)
{
TRC_ERR((TB, _T("Failed to get lock %p for FileName(W)"),
hNewData));
response = TS_CB_RESPONSE_FAIL;
dataLen = 0;
goto CB_SEND_RESPONSE;
}
if (FAILED(CBMConvertToClientPath(pOldFilename, pFilename,
newSize, fWide)))
{
response = TS_CB_RESPONSE_FAIL;
dataLen = 0;
goto CB_SEND_RESPONSE;
}
GlobalUnlock(hNewData) ;
response = TS_CB_RESPONSE_OK;
hData = hNewData ;
dataLen = newSize ;
goto CB_SEND_RESPONSE ;
}
}
/********************************************************************/
/* just get the length of the block */
/********************************************************************/
dataLen = (DCUINT32)GlobalSize(hData);
TRC_DBG((TB, _T("Got data len %d"), dataLen));
}
CB_SEND_RESPONSE:
/************************************************************************/
/* Get some memory for a message to send to the Client if necessary */
/************************************************************************/
if (hData && (dataLen != 0))
{
pduLen = dataLen + sizeof(TS_CLIP_PDU);
pClipRsp = (PTS_CLIP_PDU) LocalAlloc(LMEM_FIXED, pduLen);
if (pClipRsp == NULL)
{
TRC_ERR((TB, _T("Failed to alloc %d bytes"), pduLen));
response = TS_CB_RESPONSE_FAIL;
dataLen = 0;
pClipRsp = &clipRsp;
pduLen = sizeof(clipRsp);
}
}
else
{
TRC_DBG((TB, _T("No data to send")));
pClipRsp = &clipRsp;
pduLen = sizeof(clipRsp);
}
/************************************************************************/
/* Build the PDU */
/************************************************************************/
pClipRsp->msgType = TS_CB_FORMAT_DATA_RESPONSE;
pClipRsp->msgFlags = response;
pClipRsp->dataLen = dataLen;
/************************************************************************/
/* copy the data if necessary */
/************************************************************************/
if (dataLen != 0)
{
TRC_DBG((TB, _T("Copy %d bytes of data"), dataLen));
pData = GlobalLock(hData);
DC_MEMCPY(pClipRsp->data, pData, dataLen);
GlobalUnlock(hData);
}
/************************************************************************/
/* Close the CB if open */
/************************************************************************/
TRC_DBG((TB, _T("Closing CB")));
if (!CloseClipboard())
{
TRC_SYSTEM_ERROR("CloseClipboard");
}
CBM.open = FALSE;
/************************************************************************/
/* Send the data to the Client */
/************************************************************************/
CBMSendToClient(pClipRsp, pduLen);
/************************************************************************/
/* Free the PDU, if any */
/************************************************************************/
if (pClipRsp != &clipRsp)
{
TRC_DBG((TB, _T("Free the clip PDU")));
LocalFree(pClipRsp);
}
DC_EXIT_POINT:
if ( NULL != hDropData )
{
GlobalFree( hDropData );
}
DC_END_FN();
return;
} /* CBMOnFormatDataRequest */
//
// CBMOnFormatDataRespons
// - Client response to our request for data
/* Caller must have validated that the PDU contained enough data for the */
/* length specified in pClipPDU->dataLen */
//
DCVOID DCINTERNAL CBMOnFormatDataResponse(PTS_CLIP_PDU pClipPDU)
{
HANDLE hData = NULL;
HPDCVOID pData;
LOGPALETTE * pLogPalette = NULL;
DCUINT32 numEntries;
DCUINT32 memLen;
HRESULT hr ;
DC_BEGIN_FN("CBMOnFormatDataResponse");
/************************************************************************/
/* check the response */
/************************************************************************/
if (!(pClipPDU->msgFlags & TS_CB_RESPONSE_OK))
{
TRC_ALT((TB, _T("Got fmt data rsp failed for %d"), CBM.pendingClientID));
DC_QUIT;
}
/************************************************************************/
/* Got the data */
/************************************************************************/
TRC_NRM((TB, _T("Got OK fmt data rsp for %d"), CBM.pendingClientID));
/************************************************************************/
/* For some formats we still need to do some work */
/************************************************************************/
if (CBM.pendingClientID == CF_METAFILEPICT)
{
/********************************************************************/
/* Metafile format - create a metafile from the data */
/********************************************************************/
TRC_NRM((TB, _T("Rx data is for metafile")));
hData = CBMSetMFData(pClipPDU->dataLen, pClipPDU->data);
if (hData == NULL)
{
TRC_ERR((TB, _T("Failed to set MF data")));
}
}
else if (CBM.pendingClientID == CF_PALETTE)
{
/********************************************************************/
/* Palette format - create a palette from the data */
/********************************************************************/
/********************************************************************/
/* Allocate memory for a LOGPALETTE structure large enough to hold */
/* all the PALETTE ENTRY structures, and fill it in. */
/********************************************************************/
TRC_NRM((TB, _T("Rx data is for palette")));
numEntries = (pClipPDU->dataLen / sizeof(PALETTEENTRY));
memLen = (sizeof(LOGPALETTE) +
((numEntries - 1) * sizeof(PALETTEENTRY)));
TRC_DBG((TB, _T("%ld palette entries, allocate %ld bytes"),
numEntries, memLen));
pLogPalette = (LOGPALETTE*) GlobalAlloc(GPTR, memLen);
if (pLogPalette != NULL)
{
pLogPalette->palVersion = 0x300;
pLogPalette->palNumEntries = (WORD)numEntries;
DC_MEMCPY(pLogPalette->palPalEntry,
pClipPDU->data,
pClipPDU->dataLen);
/****************************************************************/
/* now create a palette */
/****************************************************************/
hData = CreatePalette(pLogPalette);
if (hData == NULL)
{
TRC_SYSTEM_ERROR("CreatePalette");
}
}
else
{
TRC_ERR((TB, _T("Failed to get %ld bytes"), memLen));
}
}
else
{
TRC_NRM((TB, _T("Rx data can just go on CB")));
/********************************************************************/
/* We need to copy the data, as the receive buffer will be freed on */
/* return from this function. */
/********************************************************************/
hData = GlobalAlloc(GMEM_DISCARDABLE | GMEM_MOVEABLE,
pClipPDU->dataLen);
if (hData != NULL)
{
pData = GlobalLock(hData);
if (pData != NULL)
{
TRC_NRM((TB, _T("Copy %ld bytes from %p to %p"),
pClipPDU->dataLen, pClipPDU->data, pData));
DC_MEMCPY(pData, pClipPDU->data, pClipPDU->dataLen);
GlobalUnlock(hData);
}
else
{
TRC_ERR((TB, _T("Failed to lock %p (%ld bytes)"),
hData, pClipPDU->dataLen));
GlobalFree(hData);
hData = NULL;
}
}
else
{
TRC_ERR((TB, _T("Failed to alloc %ld bytes"), pClipPDU->dataLen));
}
}
DC_EXIT_POINT:
/************************************************************************/
/* Set the state, and we're done. Note that this is done when we get a */
/* failure response too. */
/************************************************************************/
CBM_SET_STATE(CBM_STATE_LOCAL_CB_OWNER, CBM_EVENT_FORMAT_DATA_RSP);
CBM.pClipData->SetClipData(hData, CBM.pendingClientID ) ;
SetEvent(CBM.GetDataSync[TS_RECEIVE_COMPLETED]) ;
/************************************************************************/
/* tidy up */
/************************************************************************/
if (pLogPalette)
{
TRC_NRM((TB, _T("Free pLogPalette %p"), pLogPalette));
GlobalFree(pLogPalette);
}
DC_END_FN();
return;
} /* CBMOnFormatDataResponse */
/****************************************************************************/
/* CBMSendToClient */
/****************************************************************************/
DCBOOL DCINTERNAL CBMSendToClient(PTS_CLIP_PDU pClipRsp, DCUINT size)
{
BOOL fSuccess;
DWORD dwResult;
DWORD cbBytes;
DC_BEGIN_FN("CBMSendToClient");
cbBytes = size;
fSuccess = WriteFile(CBM.vcHandle,
pClipRsp,
size,
&cbBytes,
&CBM.writeOL);
if (!fSuccess)
{
dwResult = GetLastError();
if (ERROR_IO_PENDING == dwResult)
{
TRC_DBG((TB, _T("Asynchronous write")));
fSuccess = GetOverlappedResult(CBM.vcHandle,
&CBM.writeOL,
&cbBytes,
TRUE);
if (fSuccess)
{
TRC_DATA_DBG("Data sent", pClipRsp, size);
}
else
{
TRC_SYSTEM_ERROR("GetOverlappedResult failed");
}
}
else
{
TRC_ERR((TB, _T("Write failed, %#x"), dwResult));
}
}
else
{
TRC_DATA_DBG("Data sent immediately", pClipRsp, size);
}
DC_END_FN();
return(fSuccess);
} /* CBMSendToClient */
/****************************************************************************/
/* CBMGetMFData */
/****************************************************************************/
HANDLE DCINTERNAL CBMGetMFData(HANDLE hData, PDCUINT32 pDataLen)
{
DCUINT32 lenMFBits = 0;
DCBOOL rc = FALSE;
LPMETAFILEPICT pMFP = NULL;
HDC hMFDC = NULL;
HMETAFILE hMF = NULL;
HGLOBAL hMFBits = NULL;
HANDLE hNewData = NULL;
PDCUINT8 pNewData = NULL;
PDCVOID pBits = NULL;
DC_BEGIN_FN("CBMGetMFData");
TRC_NRM((TB, _T("Getting MF data")));
/************************************************************************/
/* Lock the memory to get a pointer to a METAFILEPICT header structure */
/* and create a METAFILEPICT DC. */
/************************************************************************/
pMFP = (LPMETAFILEPICT)GlobalLock(hData);
if (pMFP == NULL)
{
TRC_SYSTEM_ERROR("GlobalLock");
DC_QUIT;
}
hMFDC = CreateMetaFile(NULL);
if (hMFDC == NULL)
{
TRC_SYSTEM_ERROR("CreateMetaFile");
DC_QUIT;
}
/************************************************************************/
/* Copy the MFP by playing it into the DC and closing it. */
/************************************************************************/
if (!PlayMetaFile(hMFDC, pMFP->hMF))
{
TRC_SYSTEM_ERROR("PlayMetaFile");
CloseMetaFile(hMFDC);
DC_QUIT;
}
hMF = CloseMetaFile(hMFDC);
if (hMF == NULL)
{
TRC_SYSTEM_ERROR("CloseMetaFile");
DC_QUIT;
}
/************************************************************************/
/* Get the MF bits and determine how long they are. */
/************************************************************************/
lenMFBits = GetMetaFileBitsEx(hMF, 0, NULL);
if (lenMFBits == 0)
{
TRC_SYSTEM_ERROR("GetMetaFileBitsEx");
DC_QUIT;
}
TRC_DBG((TB, _T("length MF bits %ld"), lenMFBits));
/************************************************************************/
/* Work out how much memory we need and get a buffer */
/************************************************************************/
*pDataLen = sizeof(TS_CLIP_MFPICT) + lenMFBits;
hNewData = GlobalAlloc(GHND, *pDataLen);
if (hNewData == NULL)
{
TRC_ERR((TB, _T("Failed to get MF buffer")));
DC_QUIT;
}
pNewData = (PDCUINT8) GlobalLock(hNewData);
TRC_DBG((TB, _T("Got data to send len %ld"), *pDataLen));
/************************************************************************/
/* Copy the MF header and bits into the buffer. */
/************************************************************************/
((PTS_CLIP_MFPICT)pNewData)->mm = pMFP->mm;
((PTS_CLIP_MFPICT)pNewData)->xExt = pMFP->xExt;
((PTS_CLIP_MFPICT)pNewData)->yExt = pMFP->yExt;
lenMFBits = GetMetaFileBitsEx(hMF, lenMFBits,
(pNewData + sizeof(TS_CLIP_MFPICT)));
if (lenMFBits == 0)
{
TRC_SYSTEM_ERROR("GetMetaFileBitsEx");
DC_QUIT;
}
/************************************************************************/
/* all OK */
/************************************************************************/
TRC_NRM((TB, _T("Got %d bits of MF data"), lenMFBits));
TRC_DATA_DBG("MF bits", (pNewData + sizeof(TS_CLIP_MFPICT)), lenMFBits);
rc = TRUE;
DC_EXIT_POINT:
/************************************************************************/
/* Unlock any global mem. */
/************************************************************************/
if (pMFP)
{
GlobalUnlock(hData);
}
if (pNewData)
{
GlobalUnlock(hNewData);
}
if (hMF)
{
DeleteMetaFile(hMF);
}
/************************************************************************/
/* if things went wrong, then free the new data */
/************************************************************************/
if ((rc == FALSE) && (hNewData != NULL))
{
GlobalFree(hNewData);
hNewData = NULL;
}
DC_END_FN();
return(hNewData);
} /* CBMGetMFData */
/****************************************************************************/
/* CBMSetMFData */
/****************************************************************************/
HANDLE DCINTERNAL CBMSetMFData(DCUINT32 dataLen, PDCVOID pData)
{
DCBOOL rc = FALSE;
HGLOBAL hMFBits = NULL;
PDCVOID pMFMem = NULL;
HMETAFILE hMF = NULL;
HGLOBAL hMFPict = NULL;
LPMETAFILEPICT pMFPict = NULL;
DC_BEGIN_FN("CBMSetMFData");
TRC_DATA_DBG("Received MF data", pData, dataLen);
/************************************************************************/
/* Allocate memory to hold the MF bits (we need the handle to pass to */
/* SetMetaFileBits). */
/************************************************************************/
hMFBits = (PDCVOID)GlobalAlloc(GHND, dataLen - sizeof(TS_CLIP_MFPICT));
if (hMFBits == NULL)
{
TRC_SYSTEM_ERROR("GlobalAlloc");
DC_QUIT;
}
/************************************************************************/
/* Lock the handle and copy in the MF header. */
/************************************************************************/
pMFMem = GlobalLock(hMFBits);
if (pMFMem == NULL)
{
TRC_ERR((TB, _T("Failed to lock MF mem")));
DC_QUIT;
}
TRC_DBG((TB, _T("copying %d MF bits"), dataLen - sizeof(TS_CLIP_MFPICT) ));
DC_MEMCPY(pMFMem,
(PDCVOID)((PDCUINT8)pData + sizeof(TS_CLIP_MFPICT)),
dataLen - sizeof(TS_CLIP_MFPICT) );
GlobalUnlock(pMFMem);
/************************************************************************/
/* Now use the copied MF bits to create the actual MF bits and get a */
/* handle to the MF. */
/************************************************************************/
hMF = SetMetaFileBitsEx(dataLen - sizeof(TS_CLIP_MFPICT), (byte *) pMFMem);
if (hMF == NULL)
{
TRC_SYSTEM_ERROR("SetMetaFileBits");
DC_QUIT;
}
/************************************************************************/
/* Allocate a new METAFILEPICT structure, and use the data from the */
/* sent header. */
/************************************************************************/
hMFPict = GlobalAlloc(GHND, sizeof(METAFILEPICT));
pMFPict = (LPMETAFILEPICT)GlobalLock(hMFPict);
if (!pMFPict)
{
TRC_ERR((TB, _T("Couldn't allocate METAFILEPICT")));
DC_QUIT;
}
pMFPict->mm = (long)((PTS_CLIP_MFPICT)pData)->mm;
pMFPict->xExt = (long)((PTS_CLIP_MFPICT)pData)->xExt;
pMFPict->yExt = (long)((PTS_CLIP_MFPICT)pData)->yExt;
pMFPict->hMF = hMF;
TRC_DBG((TB, _T("Created MF size %d, %d"), pMFPict->xExt, pMFPict->yExt ));
GlobalUnlock(hMFPict);
rc = TRUE;
DC_EXIT_POINT:
/************************************************************************/
/* tidy up */
/************************************************************************/
if (rc == FALSE)
{
if (hMFPict)
{
GlobalFree(hMFPict);
}
}
if (hMFBits)
{
GlobalFree(hMFBits);
}
DC_END_FN();
return(hMFPict);
} /* CBMSetMFData */