2ea151e136
clipboard: File clipping updates
1407 lines
35 KiB
C
1407 lines
35 KiB
C
/**
|
|
* FreeRDP: A Remote Desktop Protocol Implementation
|
|
* Windows Clipboard Redirection
|
|
*
|
|
* Copyright 2012 Jason Champion
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
#include <assert.h>
|
|
#include <winpr/crt.h>
|
|
|
|
#include <freerdp/utils/event.h>
|
|
#include <winpr/stream.h>
|
|
#include <freerdp/client/cliprdr.h>
|
|
|
|
#include <Strsafe.h>
|
|
|
|
#include "wf_cliprdr.h"
|
|
|
|
extern BOOL WINAPI AddClipboardFormatListener(_In_ HWND hwnd);
|
|
extern BOOL WINAPI RemoveClipboardFormatListener(_In_ HWND hwnd);
|
|
|
|
#define WM_CLIPRDR_MESSAGE (WM_USER + 156)
|
|
#define OLE_SETCLIPBOARD 1
|
|
|
|
/* this macro will update _p pointer */
|
|
#define Read_UINT32(_p, _v) do { _v = \
|
|
(UINT32)(*_p) + \
|
|
(((UINT32)(*(_p + 1))) << 8) + \
|
|
(((UINT32)(*(_p + 2))) << 16) + \
|
|
(((UINT32)(*(_p + 3))) << 24); \
|
|
_p += 4; } while (0)
|
|
|
|
/* this macro will NOT update _p pointer */
|
|
#define Write_UINT32(_p, _v) do { \
|
|
*(_p) = (_v) & 0xFF; \
|
|
*(_p + 1) = ((_v) >> 8) & 0xFF; \
|
|
*(_p + 2) = ((_v) >> 16) & 0xFF; \
|
|
*(_p + 3) = ((_v) >> 24) & 0xFF; } while (0)
|
|
|
|
BOOL wf_create_file_obj(cliprdrContext *cliprdr, IDataObject **ppDataObject);
|
|
void wf_destroy_file_obj(IDataObject *instance);
|
|
|
|
static UINT32 get_local_format_id_by_name(cliprdrContext *cliprdr, void *format_name)
|
|
{
|
|
formatMapping *map;
|
|
int i;
|
|
|
|
for (i = 0; i < cliprdr->map_size; i++)
|
|
{
|
|
map = &cliprdr->format_mappings[i];
|
|
if ((cliprdr->capabilities & CB_USE_LONG_FORMAT_NAMES) != 0)
|
|
{
|
|
if (map->name)
|
|
{
|
|
if (memcmp(map->name, format_name, wcslen((LPCWSTR)format_name)) == 0)
|
|
return map->local_format_id;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static INLINE BOOL file_transferring(cliprdrContext *cliprdr)
|
|
{
|
|
return !!get_local_format_id_by_name(cliprdr, L"FileGroupDescriptorW");
|
|
}
|
|
|
|
static UINT32 get_remote_format_id(cliprdrContext *cliprdr, UINT32 local_format)
|
|
{
|
|
formatMapping *map;
|
|
int i;
|
|
|
|
for (i = 0; i < cliprdr->map_size; i++)
|
|
{
|
|
map = &cliprdr->format_mappings[i];
|
|
if (map->local_format_id == local_format)
|
|
{
|
|
return map->remote_format_id;
|
|
}
|
|
}
|
|
|
|
return local_format;
|
|
}
|
|
|
|
static void map_ensure_capacity(cliprdrContext *cliprdr)
|
|
{
|
|
if (cliprdr->map_size >= cliprdr->map_capacity)
|
|
{
|
|
cliprdr->format_mappings = (formatMapping *)realloc(cliprdr->format_mappings,
|
|
sizeof(formatMapping) * cliprdr->map_capacity * 2);
|
|
cliprdr->map_capacity *= 2;
|
|
}
|
|
}
|
|
|
|
static void clear_format_map(cliprdrContext *cliprdr)
|
|
{
|
|
formatMapping *map;
|
|
int i;
|
|
|
|
if (cliprdr->format_mappings)
|
|
{
|
|
for (i = 0; i < cliprdr->map_capacity; i++)
|
|
{
|
|
map = &cliprdr->format_mappings[i];
|
|
map->remote_format_id = 0;
|
|
map->local_format_id = 0;
|
|
|
|
if (map->name)
|
|
{
|
|
free(map->name);
|
|
map->name = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
cliprdr->map_size= 0;
|
|
}
|
|
/*
|
|
2.2.2.3 Client Temporary Directory PDU (CLIPRDR_TEMP_DIRECTORY)
|
|
The Temporary Directory PDU is an optional PDU sent from the client to the server.
|
|
This PDU informs the server of a location on the client file system that MUST be
|
|
used to deposit files being copied to the client. The location MUST be accessible
|
|
by the server to be useful. Section 3.1.1.3 specifies how direct file access
|
|
impacts file copy and paste.
|
|
*/
|
|
int cliprdr_send_tempdir(cliprdrContext *cliprdr)
|
|
{
|
|
RDP_CB_TEMPDIR_EVENT *cliprdr_event;
|
|
|
|
cliprdr_event = (RDP_CB_TEMPDIR_EVENT *)freerdp_event_new(CliprdrChannel_Class,
|
|
CliprdrChannel_TemporaryDirectory, NULL, NULL);
|
|
|
|
if (!cliprdr_event)
|
|
return -1;
|
|
|
|
/* Sending the TEMP path would only be valid iff the path is accessible from the server.
|
|
This should perhaps to change to a command line parameter value
|
|
*/
|
|
GetEnvironmentVariableW(L"TEMP", (LPWSTR)cliprdr_event->dirname, 260);
|
|
|
|
return freerdp_channels_send_event(cliprdr->channels, (wMessage *)cliprdr_event);
|
|
}
|
|
|
|
static void cliprdr_send_format_list(cliprdrContext *cliprdr)
|
|
{
|
|
RDP_CB_FORMAT_LIST_EVENT *cliprdr_event;
|
|
BYTE *format_data;
|
|
int format = 0;
|
|
int data_size;
|
|
int format_count;
|
|
int len = 0;
|
|
int namelen;
|
|
int stream_file_transferring = FALSE;
|
|
|
|
if (!OpenClipboard(cliprdr->hwndClipboard))
|
|
{
|
|
DEBUG_CLIPRDR("OpenClipboard failed with 0x%x", GetLastError());
|
|
return;
|
|
}
|
|
|
|
format_count = CountClipboardFormats();
|
|
data_size = format_count * (4 + MAX_PATH * 2);
|
|
|
|
format_data = (BYTE*) calloc(1, data_size);
|
|
assert(format_data != NULL);
|
|
|
|
while (format = EnumClipboardFormats(format))
|
|
{
|
|
Write_UINT32(format_data + len, format);
|
|
len += 4;
|
|
if ((cliprdr->capabilities & CB_USE_LONG_FORMAT_NAMES) != 0)
|
|
{
|
|
if (format >= CF_MAX)
|
|
{
|
|
namelen = GetClipboardFormatNameW(format, (LPWSTR)(format_data + len), MAX_PATH);
|
|
|
|
if ((wcscmp((LPWSTR)(format_data + len), L"FileNameW") == 0) ||
|
|
(wcscmp((LPWSTR)(format_data + len), L"FileName") == 0) || (wcscmp((LPWSTR)(format_data + len), CFSTR_FILEDESCRIPTORW) == 0)) {
|
|
stream_file_transferring = TRUE;
|
|
}
|
|
|
|
len += namelen * sizeof(WCHAR);
|
|
}
|
|
len += 2; /* end of Unicode string */
|
|
}
|
|
else
|
|
{
|
|
if (format >= CF_MAX)
|
|
{
|
|
static WCHAR wName[MAX_PATH] = {0};
|
|
int wLen;
|
|
|
|
ZeroMemory(wName, MAX_PATH*2);
|
|
wLen = GetClipboardFormatNameW(format, wName, MAX_PATH);
|
|
if (wLen < 16)
|
|
{
|
|
memcpy(format_data + len, wName, wLen * sizeof(WCHAR));
|
|
}
|
|
else
|
|
{
|
|
memcpy(format_data + len, wName, 32); /* truncate the long name to 32 bytes */
|
|
}
|
|
}
|
|
len += 32;
|
|
}
|
|
}
|
|
|
|
CloseClipboard();
|
|
|
|
cliprdr_event = (RDP_CB_FORMAT_LIST_EVENT *) freerdp_event_new(CliprdrChannel_Class,
|
|
CliprdrChannel_FormatList, NULL, NULL);
|
|
|
|
if (stream_file_transferring)
|
|
{
|
|
cliprdr_event->raw_format_data_size = 4 + 42;
|
|
cliprdr_event->raw_format_data = (BYTE*) calloc(1, cliprdr_event->raw_format_data_size);
|
|
format = RegisterClipboardFormatW(L"FileGroupDescriptorW");
|
|
Write_UINT32(cliprdr_event->raw_format_data, format);
|
|
wcscpy_s((WCHAR*)(cliprdr_event->raw_format_data + 4),
|
|
(cliprdr_event->raw_format_data_size - 4) / 2, L"FileGroupDescriptorW");
|
|
}
|
|
else
|
|
{
|
|
cliprdr_event->raw_format_data = (BYTE*) calloc(1, len);
|
|
assert(cliprdr_event->raw_format_data != NULL);
|
|
|
|
CopyMemory(cliprdr_event->raw_format_data, format_data, len);
|
|
cliprdr_event->raw_format_data_size = len;
|
|
}
|
|
free(format_data);
|
|
|
|
freerdp_channels_send_event(cliprdr->channels, (wMessage*) cliprdr_event);
|
|
}
|
|
|
|
int cliprdr_send_data_request(cliprdrContext *cliprdr, UINT32 format)
|
|
{
|
|
RDP_CB_DATA_REQUEST_EVENT *cliprdr_event;
|
|
int ret;
|
|
|
|
cliprdr_event = (RDP_CB_DATA_REQUEST_EVENT *)freerdp_event_new(CliprdrChannel_Class,
|
|
CliprdrChannel_DataRequest, NULL, NULL);
|
|
|
|
if (!cliprdr_event)
|
|
return -1;
|
|
|
|
cliprdr_event->format = get_remote_format_id(cliprdr, format);
|
|
|
|
ret = freerdp_channels_send_event(cliprdr->channels, (wMessage *)cliprdr_event);
|
|
|
|
if (ret != 0)
|
|
return -1;
|
|
|
|
WaitForSingleObject(cliprdr->response_data_event, INFINITE);
|
|
ResetEvent(cliprdr->response_data_event);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int cliprdr_send_lock(cliprdrContext *cliprdr)
|
|
{
|
|
RDP_CB_LOCK_CLIPDATA_EVENT *cliprdr_event;
|
|
int ret;
|
|
|
|
if ((cliprdr->capabilities & CB_CAN_LOCK_CLIPDATA) == 1) {
|
|
cliprdr_event = (RDP_CB_LOCK_CLIPDATA_EVENT *)freerdp_event_new(CliprdrChannel_Class,
|
|
CliprdrChannel_LockClipdata, NULL, NULL);
|
|
|
|
if (!cliprdr_event)
|
|
return -1;
|
|
|
|
cliprdr_event->clipDataId = 0;
|
|
|
|
ret = freerdp_channels_send_event(cliprdr->channels, (wMessage *)cliprdr_event);
|
|
|
|
if (ret != 0)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int cliprdr_send_unlock(cliprdrContext *cliprdr)
|
|
{
|
|
RDP_CB_UNLOCK_CLIPDATA_EVENT *cliprdr_event;
|
|
int ret;
|
|
|
|
if ((cliprdr->capabilities & CB_CAN_LOCK_CLIPDATA) == 1) {
|
|
cliprdr_event = (RDP_CB_UNLOCK_CLIPDATA_EVENT *)freerdp_event_new(CliprdrChannel_Class,
|
|
CliprdrChannel_UnLockClipdata, NULL, NULL);
|
|
|
|
if (!cliprdr_event)
|
|
return -1;
|
|
|
|
cliprdr_event->clipDataId = 0;
|
|
|
|
ret = freerdp_channels_send_event(cliprdr->channels, (wMessage *)cliprdr_event);
|
|
|
|
if (ret != 0)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int cliprdr_send_request_filecontents(cliprdrContext *cliprdr, void *streamid,
|
|
int index, int flag, DWORD positionhigh, DWORD positionlow, ULONG nreq)
|
|
{
|
|
RDP_CB_FILECONTENTS_REQUEST_EVENT *cliprdr_event;
|
|
int ret;
|
|
|
|
cliprdr_event = (RDP_CB_FILECONTENTS_REQUEST_EVENT *)freerdp_event_new(CliprdrChannel_Class,
|
|
CliprdrChannel_FilecontentsRequest, NULL, NULL);
|
|
|
|
if (!cliprdr_event)
|
|
return -1;
|
|
|
|
cliprdr_event->streamId = (UINT32)streamid;
|
|
cliprdr_event->lindex = index;
|
|
cliprdr_event->dwFlags = flag;
|
|
cliprdr_event->nPositionLow = positionlow;
|
|
cliprdr_event->nPositionHigh = positionhigh;
|
|
cliprdr_event->cbRequested = nreq;
|
|
cliprdr_event->clipDataId = 0;
|
|
|
|
ret = freerdp_channels_send_event(cliprdr->channels, (wMessage *)cliprdr_event);
|
|
|
|
if (ret != 0)
|
|
return -1;
|
|
|
|
WaitForSingleObject(cliprdr->req_fevent, INFINITE);
|
|
ResetEvent(cliprdr->req_fevent);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int cliprdr_send_response_filecontents(cliprdrContext *cliprdr, UINT32 streamid, UINT32 size, BYTE *data)
|
|
{
|
|
RDP_CB_FILECONTENTS_RESPONSE_EVENT *cliprdr_event;
|
|
int ret;
|
|
|
|
cliprdr_event = (RDP_CB_FILECONTENTS_RESPONSE_EVENT *)freerdp_event_new(CliprdrChannel_Class,
|
|
CliprdrChannel_FilecontentsResponse, NULL, NULL);
|
|
|
|
if (!cliprdr_event)
|
|
return -1;
|
|
|
|
cliprdr_event->streamId = streamid;
|
|
cliprdr_event->size = size;
|
|
cliprdr_event->data = data;
|
|
|
|
ret = freerdp_channels_send_event(cliprdr->channels, (wMessage *)cliprdr_event);
|
|
|
|
if (ret != 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static LRESULT CALLBACK cliprdr_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
static cliprdrContext *cliprdr = NULL;
|
|
|
|
switch (Msg)
|
|
{
|
|
case WM_CREATE:
|
|
DEBUG_CLIPRDR("info: %s - WM_CREATE", __FUNCTION__);
|
|
cliprdr = (cliprdrContext *)((CREATESTRUCT *)lParam)->lpCreateParams;
|
|
if (!AddClipboardFormatListener(hWnd)) {
|
|
DEBUG_CLIPRDR("error: AddClipboardFormatListener failed with %#x.", GetLastError());
|
|
}
|
|
cliprdr->hwndClipboard = hWnd;
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
DEBUG_CLIPRDR("info: %s - WM_CLOSE", __FUNCTION__);
|
|
RemoveClipboardFormatListener(hWnd);
|
|
break;
|
|
|
|
case WM_CLIPBOARDUPDATE:
|
|
DEBUG_CLIPRDR("info: %s - WM_CLIPBOARDUPDATE", __FUNCTION__);
|
|
if (cliprdr->channel_initialized)
|
|
{
|
|
if ((GetClipboardOwner() != cliprdr->hwndClipboard) && (S_FALSE == OleIsCurrentClipboard(cliprdr->data_obj)))
|
|
{
|
|
if (!cliprdr->hmem)
|
|
{
|
|
cliprdr->hmem = GlobalFree(cliprdr->hmem);
|
|
}
|
|
cliprdr_send_format_list(cliprdr);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_RENDERALLFORMATS:
|
|
DEBUG_CLIPRDR("info: %s - WM_RENDERALLFORMATS", __FUNCTION__);
|
|
/* discard all contexts in clipboard */
|
|
if (!OpenClipboard(cliprdr->hwndClipboard))
|
|
{
|
|
DEBUG_CLIPRDR("OpenClipboard failed with 0x%x", GetLastError());
|
|
break;
|
|
}
|
|
EmptyClipboard();
|
|
CloseClipboard();
|
|
break;
|
|
|
|
case WM_RENDERFORMAT:
|
|
DEBUG_CLIPRDR("info: %s - WM_RENDERFORMAT", __FUNCTION__);
|
|
if (cliprdr_send_data_request(cliprdr, (UINT32)wParam) != 0)
|
|
{
|
|
DEBUG_CLIPRDR("error: cliprdr_send_data_request failed.");
|
|
break;
|
|
}
|
|
|
|
if (SetClipboardData((UINT) wParam, cliprdr->hmem) == NULL)
|
|
{
|
|
DEBUG_CLIPRDR("SetClipboardData failed with 0x%x", GetLastError());
|
|
cliprdr->hmem = GlobalFree(cliprdr->hmem);
|
|
}
|
|
/* Note: GlobalFree() is not needed when success */
|
|
break;
|
|
|
|
case WM_CLIPRDR_MESSAGE:
|
|
DEBUG_CLIPRDR("info: %s - WM_CLIPRDR_MESSAGE", __FUNCTION__);
|
|
switch (wParam)
|
|
{
|
|
case OLE_SETCLIPBOARD:
|
|
DEBUG_CLIPRDR("info: %s - OLE_SETCLIPBOARD", __FUNCTION__);
|
|
if (wf_create_file_obj(cliprdr, &cliprdr->data_obj))
|
|
if (OleSetClipboard(cliprdr->data_obj) != S_OK)
|
|
wf_destroy_file_obj(cliprdr->data_obj);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_DESTROYCLIPBOARD:
|
|
case WM_ASKCBFORMATNAME:
|
|
case WM_HSCROLLCLIPBOARD:
|
|
case WM_PAINTCLIPBOARD:
|
|
case WM_SIZECLIPBOARD:
|
|
case WM_VSCROLLCLIPBOARD:
|
|
default:
|
|
return DefWindowProc(hWnd, Msg, wParam, lParam);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
static int create_cliprdr_window(cliprdrContext *cliprdr)
|
|
{
|
|
WNDCLASSEX wnd_cls;
|
|
|
|
ZeroMemory(&wnd_cls, sizeof(WNDCLASSEX));
|
|
wnd_cls.cbSize = sizeof(WNDCLASSEX);
|
|
wnd_cls.style = CS_OWNDC;
|
|
wnd_cls.lpfnWndProc = cliprdr_proc;
|
|
wnd_cls.cbClsExtra = 0;
|
|
wnd_cls.cbWndExtra = 0;
|
|
wnd_cls.hIcon = NULL;
|
|
wnd_cls.hCursor = NULL;
|
|
wnd_cls.hbrBackground = NULL;
|
|
wnd_cls.lpszMenuName = NULL;
|
|
wnd_cls.lpszClassName = L"ClipboardHiddenMessageProcessor";
|
|
wnd_cls.hInstance = GetModuleHandle(NULL);
|
|
wnd_cls.hIconSm = NULL;
|
|
RegisterClassEx(&wnd_cls);
|
|
|
|
cliprdr->hwndClipboard = CreateWindowEx(WS_EX_LEFT,
|
|
L"ClipboardHiddenMessageProcessor",
|
|
L"rdpclip",
|
|
0, 0, 0, 0, 0, HWND_MESSAGE, NULL, GetModuleHandle(NULL), cliprdr);
|
|
|
|
if (cliprdr->hwndClipboard == NULL)
|
|
{
|
|
DEBUG_CLIPRDR("error: CreateWindowEx failed with %x.", GetLastError());
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void *cliprdr_thread_func(void *arg)
|
|
{
|
|
int ret;
|
|
MSG msg;
|
|
BOOL mcode;
|
|
cliprdrContext* cliprdr = (cliprdrContext*) arg;
|
|
|
|
OleInitialize(0);
|
|
|
|
if ((ret = create_cliprdr_window(cliprdr)) != 0)
|
|
{
|
|
DEBUG_CLIPRDR("error: create clipboard window failed.");
|
|
return NULL;
|
|
}
|
|
|
|
while ((mcode = GetMessage(&msg, 0, 0, 0)) != 0)
|
|
{
|
|
if (mcode == -1)
|
|
{
|
|
DEBUG_CLIPRDR("error: clipboard thread GetMessage failed.");
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
|
|
OleUninitialize();
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void clear_file_array(cliprdrContext *cliprdr)
|
|
{
|
|
int i;
|
|
|
|
/* clear file_names array */
|
|
if (cliprdr->file_names)
|
|
{
|
|
for (i = 0; i < cliprdr->nFiles; i++)
|
|
{
|
|
if (cliprdr->file_names[i])
|
|
{
|
|
free(cliprdr->file_names[i]);
|
|
cliprdr->file_names[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* clear fileDescriptor array */
|
|
if (cliprdr->fileDescriptor)
|
|
{
|
|
for (i = 0; i < cliprdr->nFiles; i++)
|
|
{
|
|
if (cliprdr->fileDescriptor[i])
|
|
{
|
|
free(cliprdr->fileDescriptor[i]);
|
|
cliprdr->fileDescriptor[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
cliprdr->nFiles = 0;
|
|
}
|
|
|
|
void wf_cliprdr_init(wfContext* wfc, rdpChannels* channels)
|
|
{
|
|
cliprdrContext *cliprdr;
|
|
|
|
if (!wfc->instance->settings->RedirectClipboard)
|
|
{
|
|
wfc->cliprdr_context = NULL;
|
|
DEBUG_WARN( "clipboard is not redirected.\n");
|
|
return;
|
|
}
|
|
|
|
wfc->cliprdr_context = (cliprdrContext *) calloc(1, sizeof(cliprdrContext));
|
|
cliprdr = (cliprdrContext *) wfc->cliprdr_context;
|
|
assert(cliprdr != NULL);
|
|
|
|
cliprdr->channels = channels;
|
|
cliprdr->channel_initialized = FALSE;
|
|
|
|
cliprdr->map_capacity = 32;
|
|
cliprdr->map_size = 0;
|
|
|
|
cliprdr->format_mappings = (formatMapping *)calloc(1, sizeof(formatMapping) * cliprdr->map_capacity);
|
|
assert(cliprdr->format_mappings != NULL);
|
|
|
|
cliprdr->file_array_size = 32;
|
|
cliprdr->file_names = (wchar_t **)calloc(1, cliprdr->file_array_size * sizeof(wchar_t *));
|
|
cliprdr->fileDescriptor = (FILEDESCRIPTORW **)calloc(1, cliprdr->file_array_size * sizeof(FILEDESCRIPTORW *));
|
|
|
|
cliprdr->response_data_event = CreateEvent(NULL, TRUE, FALSE, L"response_data_event");
|
|
assert(cliprdr->response_data_event != NULL);
|
|
|
|
cliprdr->req_fevent = CreateEvent(NULL, TRUE, FALSE, L"request_filecontents_event");
|
|
cliprdr->ID_FILEDESCRIPTORW = RegisterClipboardFormatW(CFSTR_FILEDESCRIPTORW);
|
|
cliprdr->ID_FILECONTENTS = RegisterClipboardFormatW(CFSTR_FILECONTENTS);
|
|
cliprdr->ID_PREFERREDDROPEFFECT = RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT);
|
|
|
|
cliprdr->cliprdr_thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)cliprdr_thread_func, cliprdr, 0, NULL);
|
|
assert(cliprdr->cliprdr_thread != NULL);
|
|
}
|
|
|
|
void wf_cliprdr_uninit(wfContext* wfc)
|
|
{
|
|
cliprdrContext *cliprdr = (cliprdrContext *) wfc->cliprdr_context;
|
|
|
|
if (!cliprdr)
|
|
return;
|
|
|
|
if (cliprdr->hwndClipboard)
|
|
PostMessage(cliprdr->hwndClipboard, WM_QUIT, 0, 0);
|
|
|
|
if (cliprdr->cliprdr_thread)
|
|
{
|
|
WaitForSingleObject(cliprdr->cliprdr_thread, INFINITE);
|
|
CloseHandle(cliprdr->cliprdr_thread);
|
|
}
|
|
|
|
if (cliprdr->response_data_event)
|
|
CloseHandle(cliprdr->response_data_event);
|
|
|
|
clear_file_array(cliprdr);
|
|
clear_format_map(cliprdr);
|
|
|
|
if (cliprdr->file_names)
|
|
free(cliprdr->file_names);
|
|
if (cliprdr->fileDescriptor)
|
|
free(cliprdr->fileDescriptor);
|
|
if (cliprdr->format_mappings)
|
|
free(cliprdr->format_mappings);
|
|
|
|
free(cliprdr);
|
|
}
|
|
|
|
static void wf_cliprdr_process_cb_clip_caps_event(wfContext *wfc, RDP_CB_CLIP_CAPS *caps_event)
|
|
{
|
|
cliprdrContext *cliprdr = (cliprdrContext *)wfc->cliprdr_context;
|
|
|
|
cliprdr->capabilities = caps_event->capabilities;
|
|
}
|
|
|
|
static void wf_cliprdr_process_cb_monitor_ready_event(wfContext *wfc, RDP_CB_MONITOR_READY_EVENT *ready_event)
|
|
{
|
|
cliprdrContext *cliprdr = (cliprdrContext *)wfc->cliprdr_context;
|
|
#if 0
|
|
/*Disabled since the current function only sends the temp directory which is not
|
|
guaranteed to be accessible to the server
|
|
*/
|
|
cliprdr_send_tempdir(cliprdr);
|
|
#endif
|
|
cliprdr->channel_initialized = TRUE;
|
|
|
|
cliprdr_send_format_list(wfc->cliprdr_context);
|
|
}
|
|
|
|
static BOOL wf_cliprdr_get_file_contents(wchar_t *file_name, BYTE *buffer, int positionLow, int positionHigh, int nRequested, unsigned int *puSize)
|
|
{
|
|
HANDLE hFile;
|
|
DWORD nGet;
|
|
|
|
if (file_name == NULL || buffer == NULL || puSize == NULL)
|
|
{
|
|
DEBUG_WARN( "get file contents Invalid Arguments.\n");
|
|
return FALSE;
|
|
}
|
|
hFile = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
SetFilePointer(hFile, positionLow, (PLONG)&positionHigh, FILE_BEGIN);
|
|
|
|
if (!ReadFile(hFile, buffer, nRequested, &nGet, NULL))
|
|
{
|
|
DWORD err = GetLastError();
|
|
DEBUG_CLIPRDR("ReadFile failed with 0x%x.", err);
|
|
}
|
|
CloseHandle(hFile);
|
|
|
|
*puSize = nGet;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* path_name has a '\' at the end. e.g. c:\newfolder\, file_name is c:\newfolder\new.txt */
|
|
static FILEDESCRIPTORW *wf_cliprdr_get_file_descriptor(WCHAR* file_name, int pathLen)
|
|
{
|
|
FILEDESCRIPTORW *fd;
|
|
HANDLE hFile;
|
|
|
|
fd = (FILEDESCRIPTORW*) malloc(sizeof(FILEDESCRIPTORW));
|
|
|
|
if (!fd)
|
|
return NULL;
|
|
|
|
ZeroMemory(fd, sizeof(FILEDESCRIPTORW));
|
|
|
|
hFile = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ,
|
|
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
free(fd);
|
|
return NULL;
|
|
}
|
|
|
|
fd->dwFlags = FD_ATTRIBUTES | FD_FILESIZE | FD_WRITESTIME | FD_PROGRESSUI;
|
|
|
|
fd->dwFileAttributes = GetFileAttributes(file_name);
|
|
|
|
if (!GetFileTime(hFile, NULL, NULL, &fd->ftLastWriteTime))
|
|
{
|
|
fd->dwFlags &= ~FD_WRITESTIME;
|
|
}
|
|
|
|
fd->nFileSizeLow = GetFileSize(hFile, &fd->nFileSizeHigh);
|
|
|
|
wcscpy_s(fd->cFileName, sizeof(fd->cFileName) / 2, file_name + pathLen);
|
|
CloseHandle(hFile);
|
|
|
|
return fd;
|
|
}
|
|
|
|
static void wf_cliprdr_array_ensure_capacity(cliprdrContext *cliprdr)
|
|
{
|
|
if (cliprdr->nFiles == cliprdr->file_array_size)
|
|
{
|
|
cliprdr->file_array_size *= 2;
|
|
cliprdr->fileDescriptor = (FILEDESCRIPTORW **)realloc(cliprdr->fileDescriptor, cliprdr->file_array_size * sizeof(FILEDESCRIPTORW *));
|
|
cliprdr->file_names = (wchar_t **)realloc(cliprdr->file_names, cliprdr->file_array_size * sizeof(wchar_t *));
|
|
}
|
|
}
|
|
|
|
static void wf_cliprdr_add_to_file_arrays(cliprdrContext *cliprdr, WCHAR *full_file_name, int pathLen)
|
|
{
|
|
/* add to name array */
|
|
cliprdr->file_names[cliprdr->nFiles] = (LPWSTR) malloc(MAX_PATH);
|
|
wcscpy_s(cliprdr->file_names[cliprdr->nFiles], MAX_PATH, full_file_name);
|
|
|
|
/* add to descriptor array */
|
|
cliprdr->fileDescriptor[cliprdr->nFiles] = wf_cliprdr_get_file_descriptor(full_file_name, pathLen);
|
|
|
|
cliprdr->nFiles++;
|
|
|
|
wf_cliprdr_array_ensure_capacity(cliprdr);
|
|
}
|
|
|
|
static void wf_cliprdr_traverse_directory(cliprdrContext *cliprdr, wchar_t *Dir, int pathLen)
|
|
{
|
|
WIN32_FIND_DATA FindFileData;
|
|
HANDLE hFind;
|
|
wchar_t DirSpec[MAX_PATH];
|
|
|
|
StringCchCopy(DirSpec,MAX_PATH,Dir);
|
|
StringCchCat(DirSpec,MAX_PATH,TEXT("\\*"));
|
|
|
|
hFind = FindFirstFile(DirSpec,&FindFileData);
|
|
|
|
if(hFind == INVALID_HANDLE_VALUE)
|
|
{
|
|
DEBUG_CLIPRDR("FindFirstFile failed with 0x%x.", GetLastError());
|
|
return;
|
|
}
|
|
|
|
while(FindNextFile(hFind, &FindFileData))
|
|
{
|
|
if((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0
|
|
&& wcscmp(FindFileData.cFileName,L".") == 0
|
|
|| wcscmp(FindFileData.cFileName,L"..") == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
|
|
if((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) !=0 )
|
|
{
|
|
wchar_t DirAdd[MAX_PATH];
|
|
|
|
StringCchCopy(DirAdd,MAX_PATH,Dir);
|
|
StringCchCat(DirAdd,MAX_PATH,TEXT("\\"));
|
|
StringCchCat(DirAdd,MAX_PATH,FindFileData.cFileName);
|
|
wf_cliprdr_add_to_file_arrays(cliprdr, DirAdd, pathLen);
|
|
wf_cliprdr_traverse_directory(cliprdr, DirAdd, pathLen);
|
|
}
|
|
else
|
|
{
|
|
WCHAR fileName[MAX_PATH];
|
|
|
|
StringCchCopy(fileName,MAX_PATH,Dir);
|
|
StringCchCat(fileName,MAX_PATH,TEXT("\\"));
|
|
StringCchCat(fileName,MAX_PATH,FindFileData.cFileName);
|
|
|
|
wf_cliprdr_add_to_file_arrays(cliprdr, fileName, pathLen);
|
|
}
|
|
}
|
|
|
|
FindClose(hFind);
|
|
}
|
|
|
|
static void wf_cliprdr_process_cb_data_request_event(wfContext* wfc, RDP_CB_DATA_REQUEST_EVENT* event)
|
|
{
|
|
HANDLE hClipdata;
|
|
int size = 0;
|
|
char* buff = NULL;
|
|
char* globlemem = NULL;
|
|
UINT32 local_format;
|
|
cliprdrContext* cliprdr = (cliprdrContext*) wfc->cliprdr_context;
|
|
RDP_CB_DATA_RESPONSE_EVENT* response_event;
|
|
|
|
local_format = event->format;
|
|
|
|
if (local_format == FORMAT_ID_PALETTE)
|
|
{
|
|
/* TODO: implement this */
|
|
DEBUG_CLIPRDR("FORMAT_ID_PALETTE is not supported yet.");
|
|
}
|
|
else if (local_format == FORMAT_ID_METAFILE)
|
|
{
|
|
/* TODO: implement this */
|
|
DEBUG_CLIPRDR("FORMAT_ID_MATEFILE is not supported yet.");
|
|
}
|
|
else if (local_format == RegisterClipboardFormatW(L"FileGroupDescriptorW"))
|
|
{
|
|
HRESULT result;
|
|
LPDATAOBJECT dataObj;
|
|
FORMATETC format_etc;
|
|
STGMEDIUM stg_medium;
|
|
DROPFILES *dropFiles;
|
|
|
|
int len;
|
|
int i;
|
|
wchar_t *wFileName;
|
|
unsigned int uSize;
|
|
|
|
DEBUG_CLIPRDR("file descriptors request.");
|
|
result = OleGetClipboard(&dataObj);
|
|
if (!SUCCEEDED(result))
|
|
{
|
|
DEBUG_CLIPRDR("OleGetClipboard failed.");
|
|
}
|
|
|
|
ZeroMemory(&format_etc, sizeof(FORMATETC));
|
|
ZeroMemory(&stg_medium, sizeof(STGMEDIUM));
|
|
|
|
/* try to get FileGroupDescriptorW struct from OLE */
|
|
format_etc.cfFormat = local_format;
|
|
format_etc.tymed = TYMED_HGLOBAL;
|
|
format_etc.dwAspect = 1;
|
|
format_etc.lindex = -1;
|
|
format_etc.ptd = 0;
|
|
|
|
result = IDataObject_GetData(dataObj, &format_etc, &stg_medium);
|
|
|
|
if (SUCCEEDED(result))
|
|
{
|
|
DEBUG_CLIPRDR("Got FileGroupDescriptorW.");
|
|
globlemem = (char *)GlobalLock(stg_medium.hGlobal);
|
|
uSize = GlobalSize(stg_medium.hGlobal);
|
|
size = uSize;
|
|
buff = (char*) malloc(uSize);
|
|
CopyMemory(buff, globlemem, uSize);
|
|
GlobalUnlock(stg_medium.hGlobal);
|
|
|
|
ReleaseStgMedium(&stg_medium);
|
|
|
|
clear_file_array(cliprdr);
|
|
}
|
|
else
|
|
{
|
|
/* get DROPFILES struct from OLE */
|
|
format_etc.cfFormat = CF_HDROP;
|
|
format_etc.tymed = TYMED_HGLOBAL;
|
|
format_etc.dwAspect = 1;
|
|
format_etc.lindex = -1;
|
|
|
|
result = IDataObject_GetData(dataObj, &format_etc, &stg_medium);
|
|
if (!SUCCEEDED(result)) {
|
|
DEBUG_CLIPRDR("dataObj->GetData failed.");
|
|
}
|
|
|
|
globlemem = (char *)GlobalLock(stg_medium.hGlobal);
|
|
|
|
if (globlemem == NULL)
|
|
{
|
|
GlobalUnlock(stg_medium.hGlobal);
|
|
|
|
ReleaseStgMedium(&stg_medium);
|
|
cliprdr->nFiles = 0;
|
|
|
|
goto exit;
|
|
}
|
|
uSize = GlobalSize(stg_medium.hGlobal);
|
|
|
|
dropFiles = (DROPFILES *)malloc(uSize);
|
|
memcpy(dropFiles, globlemem, uSize);
|
|
|
|
GlobalUnlock(stg_medium.hGlobal);
|
|
|
|
ReleaseStgMedium(&stg_medium);
|
|
|
|
clear_file_array(cliprdr);
|
|
|
|
if (dropFiles->fWide)
|
|
{
|
|
wchar_t *p;
|
|
int str_len;
|
|
int offset;
|
|
int pathLen;
|
|
|
|
/* dropFiles contains file names */
|
|
for (wFileName = (wchar_t *)((char *)dropFiles + dropFiles->pFiles); (len = wcslen(wFileName)) > 0; wFileName += len + 1)
|
|
{
|
|
/* get path name */
|
|
str_len = wcslen(wFileName);
|
|
offset = str_len;
|
|
/* find the last '\' in full file name */
|
|
for (p = wFileName + offset; *p != L'\\'; p--)
|
|
{
|
|
;
|
|
}
|
|
p += 1;
|
|
pathLen = wcslen(wFileName) - wcslen(p);
|
|
|
|
wf_cliprdr_add_to_file_arrays(cliprdr, wFileName, pathLen);
|
|
|
|
if ((cliprdr->fileDescriptor[cliprdr->nFiles - 1]->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
|
|
{
|
|
/* this is a directory */
|
|
wf_cliprdr_traverse_directory(cliprdr, wFileName, pathLen);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char *p;
|
|
for (p = (char *)((char *)dropFiles + dropFiles->pFiles); (len = strlen(p)) > 0; p += len + 1, cliprdr->nFiles++)
|
|
{
|
|
int cchWideChar;
|
|
|
|
cchWideChar = MultiByteToWideChar(CP_ACP, MB_COMPOSITE, p, len, NULL, 0);
|
|
cliprdr->file_names[cliprdr->nFiles] = (LPWSTR)malloc(cchWideChar);
|
|
MultiByteToWideChar(CP_ACP, MB_COMPOSITE, p, len, cliprdr->file_names[cliprdr->nFiles], cchWideChar);
|
|
|
|
if (cliprdr->nFiles == cliprdr->file_array_size)
|
|
{
|
|
cliprdr->file_array_size *= 2;
|
|
cliprdr->fileDescriptor = (FILEDESCRIPTORW **)realloc(cliprdr->fileDescriptor, cliprdr->file_array_size * sizeof(FILEDESCRIPTORW *));
|
|
cliprdr->file_names = (wchar_t **)realloc(cliprdr->file_names, cliprdr->file_array_size * sizeof(wchar_t *));
|
|
}
|
|
}
|
|
}
|
|
|
|
exit:
|
|
size = 4 + cliprdr->nFiles * sizeof(FILEDESCRIPTORW);
|
|
buff = (char *)malloc(size);
|
|
|
|
Write_UINT32(buff, cliprdr->nFiles);
|
|
|
|
for (i = 0; i < cliprdr->nFiles; i++)
|
|
{
|
|
if (cliprdr->fileDescriptor[i])
|
|
{
|
|
memcpy(buff + 4 + i * sizeof(FILEDESCRIPTORW), cliprdr->fileDescriptor[i], sizeof(FILEDESCRIPTORW));
|
|
}
|
|
}
|
|
}
|
|
|
|
IDataObject_Release(dataObj);
|
|
}
|
|
else
|
|
{
|
|
if (!OpenClipboard(cliprdr->hwndClipboard))
|
|
{
|
|
DEBUG_CLIPRDR("OpenClipboard failed with 0x%x", GetLastError());
|
|
return;
|
|
}
|
|
|
|
hClipdata = GetClipboardData(event->format);
|
|
|
|
if (!hClipdata)
|
|
{
|
|
DEBUG_CLIPRDR("GetClipboardData failed.");
|
|
CloseClipboard();
|
|
return;
|
|
}
|
|
|
|
globlemem = (char*) GlobalLock(hClipdata);
|
|
size = (int) GlobalSize(hClipdata);
|
|
|
|
buff = (char*) malloc(size);
|
|
memcpy(buff, globlemem, size);
|
|
|
|
GlobalUnlock(hClipdata);
|
|
|
|
CloseClipboard();
|
|
}
|
|
|
|
response_event = (RDP_CB_DATA_RESPONSE_EVENT*) freerdp_event_new(CliprdrChannel_Class,
|
|
CliprdrChannel_DataResponse, NULL, NULL);
|
|
|
|
response_event->data = (BYTE *)buff;
|
|
response_event->size = size;
|
|
|
|
freerdp_channels_send_event(cliprdr->channels, (wMessage*) response_event);
|
|
|
|
/* Note: don't free buffer here. */
|
|
}
|
|
|
|
static void wf_cliprdr_process_cb_format_list_event(wfContext *wfc, RDP_CB_FORMAT_LIST_EVENT *event)
|
|
{
|
|
cliprdrContext *cliprdr = (cliprdrContext *)wfc->cliprdr_context;
|
|
UINT32 left_size = event->raw_format_data_size;
|
|
int i = 0;
|
|
BYTE *p;
|
|
BYTE* end_mark;
|
|
BOOL format_forbidden = FALSE;
|
|
|
|
/* ignore the formats member in event struct, only parsing raw_format_data */
|
|
|
|
p = event->raw_format_data;
|
|
end_mark = p + event->raw_format_data_size;
|
|
|
|
clear_format_map(cliprdr);
|
|
|
|
if ((cliprdr->capabilities & CB_USE_LONG_FORMAT_NAMES) != 0)
|
|
{
|
|
while (left_size >= 6)
|
|
{
|
|
formatMapping *map;
|
|
BYTE* tmp;
|
|
int name_len;
|
|
|
|
map = &cliprdr->format_mappings[i++];
|
|
|
|
Read_UINT32(p, map->remote_format_id);
|
|
map->name = NULL;
|
|
|
|
/* get name_len */
|
|
for (tmp = p, name_len = 0; tmp + 1 < end_mark; tmp += 2, name_len += 2)
|
|
{
|
|
if (*((unsigned short*) tmp) == 0)
|
|
break;
|
|
}
|
|
|
|
if (name_len > 0)
|
|
{
|
|
map->name = malloc(name_len + 2);
|
|
memcpy(map->name, p, name_len + 2);
|
|
|
|
map->local_format_id = RegisterClipboardFormatW((LPCWSTR)map->name);
|
|
}
|
|
else
|
|
{
|
|
map->local_format_id = map->remote_format_id;
|
|
}
|
|
|
|
left_size -= name_len + 4 + 2;
|
|
p += name_len + 2; /* p's already +4 when Read_UINT32() is called. */
|
|
|
|
cliprdr->map_size++;
|
|
|
|
map_ensure_capacity(cliprdr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UINT32 k;
|
|
|
|
for (k = 0; k < event->raw_format_data_size / 36; k++)
|
|
{
|
|
int name_len;
|
|
formatMapping* map;
|
|
|
|
map = &cliprdr->format_mappings[i++];
|
|
|
|
Read_UINT32(p, map->remote_format_id);
|
|
map->name = NULL;
|
|
|
|
if (event->raw_format_unicode)
|
|
{
|
|
/* get name_len, in bytes, if the file name is truncated, no terminated null will be included. */
|
|
for (name_len = 0; name_len < 32; name_len += 2)
|
|
{
|
|
if (*((unsigned short*) (p + name_len)) == 0)
|
|
break;
|
|
}
|
|
|
|
if (name_len > 0)
|
|
{
|
|
map->name = calloc(1, name_len + 2);
|
|
memcpy(map->name, p, name_len);
|
|
map->local_format_id = RegisterClipboardFormatW((LPCWSTR)map->name);
|
|
}
|
|
else
|
|
{
|
|
map->local_format_id = map->remote_format_id;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* get name_len, in bytes, if the file name is truncated, no terminated null will be included. */
|
|
for (name_len = 0; name_len < 32; name_len += 1)
|
|
{
|
|
if (*((unsigned char*) (p + name_len)) == 0)
|
|
break;
|
|
}
|
|
|
|
if (name_len > 0)
|
|
{
|
|
map->name = calloc(1, name_len + 1);
|
|
memcpy(map->name, p, name_len);
|
|
map->local_format_id = RegisterClipboardFormatA((LPCSTR)map->name);
|
|
}
|
|
else
|
|
{
|
|
map->local_format_id = map->remote_format_id;
|
|
}
|
|
}
|
|
|
|
p += 32; /* p's already +4 when Read_UINT32() is called. */
|
|
cliprdr->map_size++;
|
|
map_ensure_capacity(cliprdr);
|
|
}
|
|
}
|
|
|
|
if (file_transferring(cliprdr))
|
|
{
|
|
PostMessage(cliprdr->hwndClipboard, WM_CLIPRDR_MESSAGE, OLE_SETCLIPBOARD, 0);
|
|
}
|
|
else
|
|
{
|
|
if (!OpenClipboard(cliprdr->hwndClipboard))
|
|
return;
|
|
|
|
if (EmptyClipboard())
|
|
for (i = 0; i < cliprdr->map_size; i++)
|
|
SetClipboardData(cliprdr->format_mappings[i].local_format_id, NULL);
|
|
|
|
CloseClipboard();
|
|
}
|
|
}
|
|
|
|
static void wf_cliprdr_process_cb_data_response_event(wfContext *wfc, RDP_CB_DATA_RESPONSE_EVENT *event)
|
|
{
|
|
cliprdrContext *cliprdr = (cliprdrContext *)wfc->cliprdr_context;
|
|
HANDLE hMem;
|
|
char *buff;
|
|
|
|
hMem = GlobalAlloc(GMEM_FIXED, event->size);
|
|
buff = (char *) GlobalLock(hMem);
|
|
memcpy(buff, event->data, event->size);
|
|
GlobalUnlock(hMem);
|
|
|
|
cliprdr->hmem = hMem;
|
|
SetEvent(cliprdr->response_data_event);
|
|
}
|
|
|
|
static void wf_cliprdr_process_cb_filecontents_request_event(wfContext *wfc, RDP_CB_FILECONTENTS_REQUEST_EVENT *event)
|
|
{
|
|
cliprdrContext *cliprdr = (cliprdrContext *)wfc->cliprdr_context;
|
|
UINT32 uSize = 0;
|
|
BYTE *pData = NULL;
|
|
HRESULT hRet = S_OK;
|
|
FORMATETC vFormatEtc;
|
|
LPDATAOBJECT pDataObj = NULL;
|
|
STGMEDIUM vStgMedium;
|
|
LPSTREAM pStream = NULL;
|
|
BOOL bIsStreamFile = TRUE;
|
|
static LPSTREAM pStreamStc = NULL;
|
|
static UINT32 uStreamIdStc = 0;
|
|
|
|
pData = (BYTE *)calloc(1, event->cbRequested);
|
|
if (!pData)
|
|
goto error;
|
|
|
|
hRet = OleGetClipboard(&pDataObj);
|
|
if (!SUCCEEDED(hRet))
|
|
{
|
|
DEBUG_WARN( "filecontents: get ole clipboard failed.\n");
|
|
goto error;
|
|
}
|
|
|
|
ZeroMemory(&vFormatEtc, sizeof(FORMATETC));
|
|
ZeroMemory(&vStgMedium, sizeof(STGMEDIUM));
|
|
|
|
vFormatEtc.cfFormat = cliprdr->ID_FILECONTENTS;
|
|
vFormatEtc.tymed = TYMED_ISTREAM;
|
|
vFormatEtc.dwAspect = 1;
|
|
vFormatEtc.lindex = event->lindex;
|
|
vFormatEtc.ptd = NULL;
|
|
|
|
if (uStreamIdStc != event->streamId || pStreamStc == NULL)
|
|
{
|
|
LPENUMFORMATETC pEnumFormatEtc;
|
|
ULONG CeltFetched;
|
|
|
|
FORMATETC vFormatEtc2;
|
|
if (pStreamStc != NULL)
|
|
{
|
|
IStream_Release(pStreamStc);
|
|
pStreamStc = NULL;
|
|
}
|
|
|
|
bIsStreamFile = FALSE;
|
|
|
|
hRet = IDataObject_EnumFormatEtc(pDataObj, DATADIR_GET, &pEnumFormatEtc);
|
|
if (hRet == S_OK)
|
|
{
|
|
do {
|
|
hRet = IEnumFORMATETC_Next(pEnumFormatEtc, 1, &vFormatEtc2, &CeltFetched);
|
|
if (hRet == S_OK)
|
|
{
|
|
if (vFormatEtc2.cfFormat == cliprdr->ID_FILECONTENTS)
|
|
{
|
|
hRet = IDataObject_GetData(pDataObj, &vFormatEtc, &vStgMedium);
|
|
if (hRet == S_OK)
|
|
{
|
|
pStreamStc = vStgMedium.pstm;
|
|
uStreamIdStc = event->streamId;
|
|
bIsStreamFile = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} while (hRet == S_OK);
|
|
}
|
|
}
|
|
|
|
if (bIsStreamFile == TRUE)
|
|
{
|
|
if (event->dwFlags == 0x00000001) /* FILECONTENTS_SIZE */
|
|
{
|
|
STATSTG vStatStg;
|
|
|
|
ZeroMemory(&vStatStg, sizeof(STATSTG));
|
|
|
|
hRet = IStream_Stat(pStreamStc, &vStatStg, STATFLAG_NONAME);
|
|
if (hRet == S_OK)
|
|
{
|
|
Write_UINT32(pData, vStatStg.cbSize.LowPart);
|
|
Write_UINT32(pData + 4, vStatStg.cbSize.HighPart);
|
|
uSize = event->cbRequested;
|
|
}
|
|
}
|
|
else if (event->dwFlags == 0x00000002) /* FILECONTENTS_RANGE */
|
|
{
|
|
LARGE_INTEGER dlibMove;
|
|
ULARGE_INTEGER dlibNewPosition;
|
|
|
|
dlibMove.HighPart = event->nPositionHigh;
|
|
dlibMove.LowPart = event->nPositionLow;
|
|
|
|
hRet = IStream_Seek(pStreamStc, dlibMove, STREAM_SEEK_SET, &dlibNewPosition);
|
|
if (SUCCEEDED(hRet))
|
|
{
|
|
hRet = IStream_Read(pStreamStc, pData, event->cbRequested, (PULONG)&uSize);
|
|
}
|
|
|
|
}
|
|
}
|
|
else // is local file
|
|
{
|
|
if (event->dwFlags == 0x00000001) /* FILECONTENTS_SIZE */
|
|
{
|
|
Write_UINT32(pData, cliprdr->fileDescriptor[event->lindex]->nFileSizeLow);
|
|
Write_UINT32(pData + 4, cliprdr->fileDescriptor[event->lindex]->nFileSizeHigh);
|
|
uSize = event->cbRequested;
|
|
}
|
|
else if (event->dwFlags == 0x00000002) /* FILECONTENTS_RANGE */
|
|
{
|
|
BOOL bRet;
|
|
|
|
bRet = wf_cliprdr_get_file_contents(cliprdr->file_names[event->lindex], pData,
|
|
event->nPositionLow, event->nPositionHigh, event->cbRequested, &uSize);
|
|
if (bRet == FALSE)
|
|
{
|
|
DEBUG_WARN( "get file contents failed.\n");
|
|
uSize = 0;
|
|
goto error;
|
|
}
|
|
}
|
|
}
|
|
|
|
IDataObject_Release(pDataObj);
|
|
|
|
if (uSize == 0)
|
|
{
|
|
free(pData);
|
|
pData = NULL;
|
|
}
|
|
|
|
cliprdr_send_response_filecontents(cliprdr, event->streamId, uSize, pData);
|
|
|
|
return;
|
|
|
|
error:
|
|
if (pData)
|
|
{
|
|
free(pData);
|
|
pData = NULL;
|
|
}
|
|
|
|
if (pDataObj)
|
|
{
|
|
IDataObject_Release(pDataObj);
|
|
pDataObj = NULL;
|
|
}
|
|
DEBUG_WARN( "filecontents: send failed response.\n");
|
|
cliprdr_send_response_filecontents(cliprdr, event->streamId, 0, NULL);
|
|
return;
|
|
}
|
|
|
|
static void wf_cliprdr_process_cb_filecontents_response_event(wfContext *wfc, RDP_CB_FILECONTENTS_RESPONSE_EVENT *event)
|
|
{
|
|
cliprdrContext *cliprdr = (cliprdrContext *)wfc->cliprdr_context;
|
|
|
|
cliprdr->req_fsize = event->size;
|
|
cliprdr->req_fdata = (char *)malloc(event->size);
|
|
memcpy(cliprdr->req_fdata, event->data, event->size);
|
|
|
|
SetEvent(cliprdr->req_fevent);
|
|
}
|
|
|
|
static void wf_cliprdr_process_cb_lock_clipdata_event(wfContext *wfc, RDP_CB_LOCK_CLIPDATA_EVENT *event)
|
|
{
|
|
|
|
}
|
|
|
|
static void wf_cliprdr_process_cb_unlock_clipdata_event(wfContext *wfc, RDP_CB_UNLOCK_CLIPDATA_EVENT *event)
|
|
{
|
|
|
|
}
|
|
|
|
void wf_process_cliprdr_event(wfContext *wfc, wMessage *event)
|
|
{
|
|
switch (GetMessageType(event->id))
|
|
{
|
|
case CliprdrChannel_ClipCaps:
|
|
wf_cliprdr_process_cb_clip_caps_event(wfc, (RDP_CB_CLIP_CAPS *)event);
|
|
break;
|
|
|
|
case CliprdrChannel_MonitorReady:
|
|
wf_cliprdr_process_cb_monitor_ready_event(wfc, (RDP_CB_MONITOR_READY_EVENT *)event);
|
|
break;
|
|
|
|
case CliprdrChannel_FormatList:
|
|
wf_cliprdr_process_cb_format_list_event(wfc, (RDP_CB_FORMAT_LIST_EVENT *) event);
|
|
break;
|
|
|
|
case CliprdrChannel_DataRequest:
|
|
wf_cliprdr_process_cb_data_request_event(wfc, (RDP_CB_DATA_REQUEST_EVENT *) event);
|
|
break;
|
|
|
|
case CliprdrChannel_DataResponse:
|
|
wf_cliprdr_process_cb_data_response_event(wfc, (RDP_CB_DATA_RESPONSE_EVENT *) event);
|
|
break;
|
|
|
|
case CliprdrChannel_FilecontentsRequest:
|
|
wf_cliprdr_process_cb_filecontents_request_event(wfc, (RDP_CB_FILECONTENTS_REQUEST_EVENT *) event);
|
|
break;
|
|
|
|
case CliprdrChannel_FilecontentsResponse:
|
|
wf_cliprdr_process_cb_filecontents_response_event(wfc, (RDP_CB_FILECONTENTS_RESPONSE_EVENT *) event);
|
|
break;
|
|
|
|
case CliprdrChannel_LockClipdata:
|
|
wf_cliprdr_process_cb_lock_clipdata_event(wfc, (RDP_CB_LOCK_CLIPDATA_EVENT *) event);
|
|
break;
|
|
|
|
case CliprdrChannel_UnLockClipdata:
|
|
wf_cliprdr_process_cb_unlock_clipdata_event(wfc, (RDP_CB_UNLOCK_CLIPDATA_EVENT *) event);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
BOOL wf_cliprdr_process_selection_notify(wfContext* wfc, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL wf_cliprdr_process_selection_request(wfContext* wfc, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL wf_cliprdr_process_selection_clear(wfContext* wfc, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL wf_cliprdr_process_property_notify(wfContext* wfc, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
void wf_cliprdr_check_owner(wfContext* wfc)
|
|
{
|
|
|
|
}
|