/**MOD+**********************************************************************/ /* Module: sclip2.cpp */ /* */ /* Purpose: Second thread */ /* Receives RDP clipboard messages */ /* */ /* Copyright(C) Microsoft Corporation 1998 */ /* */ /**MOD-**********************************************************************/ #include #define TRC_GROUP TRC_GROUP_CORE #define TRC_FILE "sclip2" #include #include #include #include #include /****************************************************************************/ /* Global data */ /****************************************************************************/ #include // // 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 */