/*! * @file clipboard.h * @brief Definitions for clipboard interaction functionality. */ #include "extapi.h" #include "clipboard.h" #ifdef _WIN32 /*! @brief GlobalAlloc function pointer type. */ typedef HGLOBAL (WINAPI * PGLOBALALLOC)( UINT uFlags, SIZE_T dwBytes ); /*! @brief GlobalFree function pointer type. */ typedef HGLOBAL (WINAPI * PGLOBALFREE)( HGLOBAL hMem ); /*! @brief GlobalLock function pointer type. */ typedef LPVOID (WINAPI * PGLOBALLOCK)( HGLOBAL hMem ); /*! @brief GlobalUnlock function pointer type. */ typedef LPVOID (WINAPI * PGLOBALUNLOCK)( HGLOBAL hMem ); /*! @brief OpenClipboard function pointer type. */ typedef BOOL (WINAPI * POPENCLIPBOARD)( HWND hWndNewOwner ); /*! @brief CloseClipboard function pointer type. */ typedef BOOL (WINAPI * PCLOSECLIPBOARD)(); /*! @brief SetClipboardData function pointer type. */ typedef HANDLE (WINAPI * PSETCLIPBOARDDATA)( UINT uFormat, HANDLE hMem ); /*! @brief SetClipboardData function pointer type. */ typedef HANDLE (WINAPI * PGETCLIPBOARDDATA)( UINT uFormat ); /*! @brief EnumClipboardFormats function pointer type. */ typedef UINT (WINAPI * PENUMCLIPBOARDFORMATS)( UINT uFormat ); /*! @brief EmptyClipboard function pointer type. */ typedef BOOL (WINAPI * PEMPTYCLIPBOARD)(); /*! @brief DragQueryFileA function pointer type. */ typedef BOOL (WINAPI * PDRAGQUERYFILEA)( HDROP hDrop, UINT iFile, LPSTR lpszFile, UINT cch ); /*! @brief CreateFileA function pointer type. */ typedef HANDLE (WINAPI * PCREATEFILEA)( LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile ); /*! @brief CloseHandle function pointer type. */ typedef BOOL (WINAPI * PCLOSEHANDLE)( HANDLE hObject ); /*! @brief GetFileSizeEx function pointer type. */ typedef BOOL (WINAPI * PGETFILESIZEEX)( HANDLE hFile, PLARGE_INTEGER lpFileSize ); #endif /*! * @brief Handle the request to get the data from the clipboard. * @details This function currently only supports the following clipboard data formats: * - CF_TEXT - raw text data. * - CF_HDROP - file selection. * * Over time more formats will be supported. * @param remote Pointer to the remote endpoint. * @param packet Pointer to the request packet. * @return Indication of success or failure. * @todo Add support for more data formats. */ DWORD request_clipboard_get_data( Remote *remote, Packet *packet ) { #ifdef _WIN32 DWORD dwResult; HMODULE hKernel32 = NULL; HMODULE hUser32 = NULL; HMODULE hShell32 = NULL; PGLOBALLOCK pGlobalLock = NULL; PGLOBALUNLOCK pGlobalUnlock = NULL; POPENCLIPBOARD pOpenClipboard = NULL; PCLOSECLIPBOARD pCloseClipboard = NULL; PGETCLIPBOARDDATA pGetClipboardData = NULL; PENUMCLIPBOARDFORMATS pEnumClipboardFormats = NULL; PDRAGQUERYFILEA pDragQueryFileA = NULL; PCREATEFILEA pCreateFileA = NULL; PCLOSEHANDLE pCloseHandle = NULL; PGETFILESIZEEX pGetFileSizeEx = NULL; HANDLE hSourceFile = NULL; PCHAR lpClipString = NULL; HGLOBAL hClipboardData = NULL; HDROP hFileDrop = NULL; UINT uFormat = 0; UINT uFileIndex = 0; UINT uFileCount = 0; CHAR lpFileName[MAX_PATH]; Tlv entries[2] = {0}; LARGE_INTEGER largeInt = {0}; Packet *pResponse = packet_create_response( packet ); do { dprintf( "Loading user32.dll" ); if( (hUser32 = LoadLibraryA( "user32.dll" )) == NULL) BREAK_ON_ERROR( "Unable to load user32.dll" ); dprintf( "Loading kernel32.dll" ); if( (hKernel32 = LoadLibraryA( "kernel32.dll" )) == NULL) BREAK_ON_ERROR( "Unable to load kernel32.dll" ); dprintf( "Searching for GlobalLock" ); if( (pGlobalLock = (PGLOBALLOCK)GetProcAddress( hKernel32, "GlobalLock" )) == NULL ) BREAK_ON_ERROR( "Unable to locate GlobalLock in kernel32.dll" ); dprintf( "Searching for GlobalUnlock" ); if( (pGlobalUnlock = (PGLOBALUNLOCK)GetProcAddress( hKernel32, "GlobalUnlock" )) == NULL ) BREAK_ON_ERROR( "Unable to locate GlobalUnlock in kernel32.dll" ); dprintf( "Searching for OpenClipboard" ); if( (pOpenClipboard = (POPENCLIPBOARD)GetProcAddress( hUser32, "OpenClipboard" )) == NULL ) BREAK_ON_ERROR( "Unable to locate OpenClipboard in user32.dll" ); dprintf( "Searching for CloseClipboard" ); if( (pCloseClipboard = (PCLOSECLIPBOARD)GetProcAddress( hUser32, "CloseClipboard" )) == NULL ) BREAK_ON_ERROR( "Unable to locate CloseClipboard in user32.dll" ); dprintf( "Searching for GetClipboardData" ); if( (pGetClipboardData = (PGETCLIPBOARDDATA)GetProcAddress( hUser32, "GetClipboardData" )) == NULL ) BREAK_ON_ERROR( "Unable to locate GetClipboardData in user32.dll" ); dprintf( "Searching for EnumClipboardFormats" ); if( (pEnumClipboardFormats = (PENUMCLIPBOARDFORMATS)GetProcAddress( hUser32, "EnumClipboardFormats" )) == NULL ) BREAK_ON_ERROR( "Unable to locate EnumClipboardFormats in user32.dll" ); // Try to get a lock on the clipboard if( !pOpenClipboard( NULL ) ) { dwResult = GetLastError(); BREAK_WITH_ERROR( "Unable to open the clipboard", dwResult ); } dprintf( "Clipboard locked, attempting to get data..." ); while ( uFormat = pEnumClipboardFormats( uFormat ) ) { if( uFormat == CF_TEXT ) { // there's raw text on the clipboard if ( (hClipboardData = pGetClipboardData( CF_TEXT ) ) != NULL && (lpClipString = (PCHAR)pGlobalLock( hClipboardData )) != NULL ) { dprintf( "Clipboard text captured: %s", lpClipString ); packet_add_tlv_string( pResponse, TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT, lpClipString ); pGlobalUnlock( hClipboardData ); } } else if( uFormat == CF_HDROP ) { // there's one or more files on the clipboard dprintf( "Files have been located on the clipboard" ); do { dprintf( "Loading shell32.dll" ); if( (hShell32 = LoadLibraryA( "shell32.dll" )) == NULL) BREAK_ON_ERROR( "Unable to load shell32.dll" ); dprintf( "Searching for CreateFileA" ); if( (pCreateFileA = (PCREATEFILEA)GetProcAddress( hKernel32, "CreateFileA" )) == NULL ) BREAK_ON_ERROR( "Unable to locate CreateFileA in kernel32.dll" ); dprintf( "Searching for CloseHandle" ); if( (pCloseHandle = (PCLOSEHANDLE)GetProcAddress( hKernel32, "CloseHandle" )) == NULL ) BREAK_ON_ERROR( "Unable to locate CloseHandle in kernel32.dll" ); dprintf( "Searching for GetFileSizeEx" ); if( (pGetFileSizeEx = (PGETFILESIZEEX)GetProcAddress( hKernel32, "GetFileSizeEx" )) == NULL ) BREAK_ON_ERROR( "Unable to locate GetFileSizeEx in kernel32.dll" ); dprintf( "Searching for DragQueryFileA" ); if( (pDragQueryFileA = (PDRAGQUERYFILEA)GetProcAddress( hShell32, "DragQueryFileA" )) == NULL ) BREAK_ON_ERROR( "Unable to locate CloseClipboard in shell32.dll" ); dprintf( "Grabbing the clipboard file drop data" ); if ( (hClipboardData = pGetClipboardData( CF_HDROP ) ) != NULL && (hFileDrop = (HDROP)pGlobalLock( hClipboardData )) != NULL ) { uFileCount = pDragQueryFileA( hFileDrop, (UINT)-1, NULL, 0 ); dprintf( "Parsing %u file(s) on the clipboard.", uFileCount ); for( uFileIndex = 0; uFileIndex < uFileCount; ++uFileIndex ) { if( pDragQueryFileA( hFileDrop, uFileIndex, lpFileName, sizeof( lpFileName ) ) ) { dprintf( "Clipboard file entry: %s", lpFileName ); memset( &entries, 0, sizeof(entries) ); memset( &largeInt, 0, sizeof(largeInt) ); entries[0].header.type = TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_NAME; entries[0].header.length = (DWORD)strlen( lpFileName ) + 1; entries[0].buffer = (PUCHAR)lpFileName; entries[1].header.type = TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_SIZE; entries[1].header.length = sizeof(QWORD); entries[1].buffer = (PUCHAR)&largeInt.QuadPart; if( (hSourceFile = pCreateFileA( lpFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL )) != NULL ) { if( pGetFileSizeEx( hSourceFile, &largeInt ) ) { largeInt.QuadPart = htonq( largeInt.QuadPart ); } pCloseHandle( hSourceFile ); } packet_add_tlv_group( pResponse, TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE, entries, 2 ); } } pGlobalUnlock( hClipboardData ); } } while(0); } } dwResult = GetLastError(); pCloseClipboard(); } while(0); if( hShell32 ) FreeLibrary( hShell32 ); if( hKernel32 ) FreeLibrary( hKernel32 ); if( hUser32 ) FreeLibrary( hUser32 ); if( pResponse ) packet_transmit_response( dwResult, remote, pResponse ); return dwResult; #else return ERROR_NOT_SUPPORTED; #endif } /*! * @brief Handle the request to set the data that's on the clipboard. * @details This function currently only supports the following clipboard data formats: * - CF_TEXT - raw text data. * * Over time more formats will be supported. * @param remote Pointer to the remote endpoint. * @param packet Pointer to the request packet. * @return Indication of success or failure. * @todo Add support for more data formats. */ DWORD request_clipboard_set_data( Remote *remote, Packet *packet ) { #ifdef _WIN32 DWORD dwResult; HMODULE hKernel32 = NULL; HMODULE hUser32 = NULL; PGLOBALALLOC pGlobalAlloc = NULL; PGLOBALFREE pGlobalFree = NULL; PGLOBALLOCK pGlobalLock = NULL; PGLOBALUNLOCK pGlobalUnlock = NULL; POPENCLIPBOARD pOpenClipboard = NULL; PCLOSECLIPBOARD pCloseClipboard = NULL; PSETCLIPBOARDDATA pSetClipboardData = NULL; PEMPTYCLIPBOARD pEmptyClipboard = NULL; PCHAR lpClipString; HGLOBAL hClipboardData; PCHAR lpLockedData; SIZE_T cbStringBytes; do { if( (lpClipString = packet_get_tlv_value_string( packet, TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT )) == NULL ) BREAK_WITH_ERROR( "No string data specified", ERROR_INVALID_PARAMETER ); dprintf( "Loading user32.dll" ); if( (hUser32 = LoadLibraryA( "user32.dll" )) == NULL) BREAK_ON_ERROR( "Unable to load user32.dll" ); dprintf( "Loading kernel32.dll" ); if( (hKernel32 = LoadLibraryA( "kernel32.dll" )) == NULL) BREAK_ON_ERROR( "Unable to load kernel32.dll" ); dprintf( "Searching for GlobalAlloc" ); if( (pGlobalAlloc = (PGLOBALALLOC)GetProcAddress( hKernel32, "GlobalAlloc" )) == NULL ) BREAK_ON_ERROR( "Unable to locate GlobalAlloc in kernel32.dll" ); dprintf( "Searching for GlobalLock" ); if( (pGlobalLock = (PGLOBALLOCK)GetProcAddress( hKernel32, "GlobalLock" )) == NULL ) BREAK_ON_ERROR( "Unable to locate GlobalLock in kernel32.dll" ); dprintf( "Searching for GlobalUnlock" ); if( (pGlobalUnlock = (PGLOBALUNLOCK)GetProcAddress( hKernel32, "GlobalUnlock" )) == NULL ) BREAK_ON_ERROR( "Unable to locate GlobalUnlock in kernel32.dll" ); dprintf( "Searching for OpenClipboard" ); if( (pOpenClipboard = (POPENCLIPBOARD)GetProcAddress( hUser32, "OpenClipboard" )) == NULL ) BREAK_ON_ERROR( "Unable to locate OpenClipboard in user32.dll" ); dprintf( "Searching for CloseClipboard" ); if( (pCloseClipboard = (PCLOSECLIPBOARD)GetProcAddress( hUser32, "CloseClipboard" )) == NULL ) BREAK_ON_ERROR( "Unable to locate CloseClipboard in user32.dll" ); dprintf( "Searching for EmptyClipboard" ); if( (pEmptyClipboard = (PEMPTYCLIPBOARD)GetProcAddress( hUser32, "EmptyClipboard" )) == NULL ) BREAK_ON_ERROR( "Unable to locate EmptyClipboard in user32.dll" ); dprintf( "Searching for SetClipboardData" ); if( (pSetClipboardData = (PSETCLIPBOARDDATA)GetProcAddress( hUser32, "SetClipboardData" )) == NULL ) BREAK_ON_ERROR( "Unable to locate SetClipboardData in user32.dll" ); cbStringBytes = (SIZE_T)strlen( lpClipString ) + 1; // do the "use the right kind of memory once locked" clip board data dance. // Note that we don't free up the memory we've allocated with GlobalAlloc // because the windows clipboard magic does it for us. if( (hClipboardData = pGlobalAlloc( GMEM_MOVEABLE | GMEM_DDESHARE, cbStringBytes )) == NULL ) { dwResult = GetLastError(); pCloseClipboard(); BREAK_WITH_ERROR( "Failed to allocate clipboard memory", dwResult ); } lpLockedData = (PCHAR)pGlobalLock( hClipboardData ); memcpy_s( lpLockedData, cbStringBytes, lpClipString, cbStringBytes ); pGlobalUnlock( hClipboardData ); // Try to get a lock on the clipboard if( !pOpenClipboard( NULL ) ) { dwResult = GetLastError(); BREAK_WITH_ERROR( "Unable to open the clipboard", dwResult ); } // Clear the clipboard data pEmptyClipboard(); if( !pSetClipboardData( CF_TEXT, hClipboardData ) ) { dwResult = GetLastError(); dprintf( "Failed to set the clipboad data: %u", dwResult ); } else { dwResult = ERROR_SUCCESS; } pCloseClipboard(); } while(0); // If something went wrong and we have clipboard data, then we need to // free it up because the clipboard can't do it for us. if( dwResult != ERROR_SUCCESS && hClipboardData != NULL ) { dprintf( "Searching for GlobalFree" ); if( (pGlobalFree = (PGLOBALFREE)GetProcAddress( hKernel32, "GlobalFree" )) != NULL ) pGlobalFree( hClipboardData ); } if( hKernel32 ) FreeLibrary( hKernel32 ); if( hUser32 ) FreeLibrary( hUser32 ); packet_transmit_empty_response( remote, packet, dwResult ); return dwResult; #else return ERROR_NOT_SUPPORTED; #endif }