FreeRDP
Loading...
Searching...
No Matches
wf_cliprdr.c
1
23#include <freerdp/config.h>
24
25#define CINTERFACE
26#define COBJMACROS
27
28#include <ole2.h>
29#include <shlobj.h>
30#include <windows.h>
31#include <winuser.h>
32
33#include <winpr/assert.h>
34#include <winpr/library.h>
35
36#include <winpr/crt.h>
37#include <winpr/tchar.h>
38#include <winpr/stream.h>
39
40#include <freerdp/log.h>
41#include <freerdp/client/cliprdr.h>
42
43#include <strsafe.h>
44
45#include "wf_cliprdr.h"
46
47#define TAG CLIENT_TAG("windows")
48
49#ifdef WITH_DEBUG_CLIPRDR
50#define DEBUG_CLIPRDR(...) WLog_DBG(TAG, __VA_ARGS__)
51#else
52#define DEBUG_CLIPRDR(...) \
53 do \
54 { \
55 } while (0)
56#endif
57
58typedef BOOL(WINAPI* fnAddClipboardFormatListener)(HWND hwnd);
59typedef BOOL(WINAPI* fnRemoveClipboardFormatListener)(HWND hwnd);
60typedef BOOL(WINAPI* fnGetUpdatedClipboardFormats)(PUINT lpuiFormats, UINT cFormats,
61 PUINT pcFormatsOut);
62
63typedef struct
64{
65 UINT32 remote_format_id;
66 UINT32 local_format_id;
67 WCHAR* name;
68} formatMapping;
69
70typedef struct
71{
72 IEnumFORMATETC iEnumFORMATETC;
73
74 LONG m_lRefCount;
75 LONG m_nIndex;
76 LONG m_nNumFormats;
77 FORMATETC* m_pFormatEtc;
78} CliprdrEnumFORMATETC;
79
80typedef struct
81{
82 IStream iStream;
83
84 LONG m_lRefCount;
85 ULONG m_lIndex;
86 ULARGE_INTEGER m_lSize;
87 ULARGE_INTEGER m_lOffset;
88 FILEDESCRIPTORW m_Dsc;
89 void* m_pData;
90} CliprdrStream;
91
92typedef struct
93{
94 IDataObject iDataObject;
95
96 LONG m_lRefCount;
97 FORMATETC* m_pFormatEtc;
98 STGMEDIUM* m_pStgMedium;
99 ULONG m_nNumFormats;
100 ULONG m_nStreams;
101 IStream** m_pStream;
102 void* m_pData;
103} CliprdrDataObject;
104
105typedef struct
106{
107 wfContext* wfc;
108 rdpChannels* channels;
109 CliprdrClientContext* context;
110
111 BOOL sync;
112 UINT32 capabilities;
113
114 size_t map_size;
115 size_t map_capacity;
116 formatMapping* format_mappings;
117
118 UINT32 requestedFormatId;
119
120 HWND hwnd;
121 HANDLE hmem;
122 HANDLE thread;
123 HANDLE response_data_event;
124
125 LPDATAOBJECT data_obj;
126 ULONG req_fsize;
127 char* req_fdata;
128 HANDLE req_fevent;
129
130 size_t nFiles;
131 size_t file_array_size;
132 WCHAR** file_names;
133 FILEDESCRIPTORW** fileDescriptor;
134
135 BOOL legacyApi;
136 HMODULE hUser32;
137 HWND hWndNextViewer;
138 fnAddClipboardFormatListener AddClipboardFormatListener;
139 fnRemoveClipboardFormatListener RemoveClipboardFormatListener;
140 fnGetUpdatedClipboardFormats GetUpdatedClipboardFormats;
141} wfClipboard;
142
143#define WM_CLIPRDR_MESSAGE (WM_USER + 156)
144#define OLE_SETCLIPBOARD 1
145
146static BOOL wf_create_file_obj(wfClipboard* cliprdrrdr, IDataObject** ppDataObject);
147static void wf_destroy_file_obj(IDataObject* instance);
148static UINT32 get_remote_format_id(wfClipboard* clipboard, UINT32 local_format);
149static UINT cliprdr_send_data_request(wfClipboard* clipboard, UINT32 format);
150static UINT cliprdr_send_lock(wfClipboard* clipboard);
151static UINT cliprdr_send_unlock(wfClipboard* clipboard);
152static UINT cliprdr_send_request_filecontents(wfClipboard* clipboard, const void* streamid,
153 ULONG index, UINT32 flag, UINT64 position,
154 ULONG request);
155
156static void CliprdrDataObject_Delete(CliprdrDataObject* instance);
157
158static CliprdrEnumFORMATETC* CliprdrEnumFORMATETC_New(ULONG nFormats, FORMATETC* pFormatEtc);
159static void CliprdrEnumFORMATETC_Delete(CliprdrEnumFORMATETC* instance);
160
161static void CliprdrStream_Delete(CliprdrStream* instance);
162
163static BOOL try_open_clipboard(HWND hwnd)
164{
165 for (size_t x = 0; x < 10; x++)
166 {
167 if (OpenClipboard(hwnd))
168 return TRUE;
169 Sleep(10);
170 }
171 return FALSE;
172}
173
178static HRESULT STDMETHODCALLTYPE CliprdrStream_QueryInterface(IStream* This, REFIID riid,
179 void** ppvObject)
180{
181 if (IsEqualIID(riid, &IID_IStream) || IsEqualIID(riid, &IID_IUnknown))
182 {
183 IStream_AddRef(This);
184 *ppvObject = This;
185 return S_OK;
186 }
187 else
188 {
189 *ppvObject = 0;
190 return E_NOINTERFACE;
191 }
192}
193
194static ULONG STDMETHODCALLTYPE CliprdrStream_AddRef(IStream* This)
195{
196 CliprdrStream* instance = (CliprdrStream*)This;
197
198 if (!instance)
199 return 0;
200
201 return InterlockedIncrement(&instance->m_lRefCount);
202}
203
204static ULONG STDMETHODCALLTYPE CliprdrStream_Release(IStream* This)
205{
206 LONG count;
207 CliprdrStream* instance = (CliprdrStream*)This;
208
209 if (!instance)
210 return 0;
211
212 count = InterlockedDecrement(&instance->m_lRefCount);
213
214 if (count == 0)
215 {
216 CliprdrStream_Delete(instance);
217 return 0;
218 }
219 else
220 {
221 return count;
222 }
223}
224
225static HRESULT STDMETHODCALLTYPE CliprdrStream_Read(IStream* This, void* pv, ULONG cb,
226 ULONG* pcbRead)
227{
228 CliprdrStream* instance = (CliprdrStream*)This;
229
230 if (!pv || !pcbRead || !instance)
231 return E_INVALIDARG;
232
233 wfClipboard* clipboard = (wfClipboard*)instance->m_pData;
234 *pcbRead = 0;
235
236 if (instance->m_lOffset.QuadPart >= instance->m_lSize.QuadPart)
237 return S_FALSE;
238
239 const int ret =
240 cliprdr_send_request_filecontents(clipboard, (void*)This, instance->m_lIndex,
241 FILECONTENTS_RANGE, instance->m_lOffset.QuadPart, cb);
242
243 if (ret < 0)
244 return E_FAIL;
245
246 if (clipboard->req_fsize > cb)
247 return E_FAIL;
248
249 if (clipboard->req_fdata)
250 {
251 CopyMemory(pv, clipboard->req_fdata, clipboard->req_fsize);
252 free(clipboard->req_fdata);
253 }
254
255 *pcbRead = clipboard->req_fsize;
256 instance->m_lOffset.QuadPart += clipboard->req_fsize;
257
258 if (clipboard->req_fsize < cb)
259 return S_FALSE;
260
261 return S_OK;
262}
263
264static HRESULT STDMETHODCALLTYPE CliprdrStream_Write(IStream* This, const void* pv, ULONG cb,
265 ULONG* pcbWritten)
266{
267 (void)This;
268 (void)pv;
269 (void)cb;
270 (void)pcbWritten;
271 return STG_E_ACCESSDENIED;
272}
273
274static HRESULT STDMETHODCALLTYPE CliprdrStream_Seek(IStream* This, LARGE_INTEGER dlibMove,
275 DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition)
276{
277 ULONGLONG newoffset;
278 CliprdrStream* instance = (CliprdrStream*)This;
279
280 if (!instance)
281 return E_INVALIDARG;
282
283 newoffset = instance->m_lOffset.QuadPart;
284
285 switch (dwOrigin)
286 {
287 case STREAM_SEEK_SET:
288 newoffset = dlibMove.QuadPart;
289 break;
290
291 case STREAM_SEEK_CUR:
292 newoffset += dlibMove.QuadPart;
293 break;
294
295 case STREAM_SEEK_END:
296 newoffset = instance->m_lSize.QuadPart + dlibMove.QuadPart;
297 break;
298
299 default:
300 return E_INVALIDARG;
301 }
302
303 if (newoffset < 0 || newoffset >= instance->m_lSize.QuadPart)
304 return E_FAIL;
305
306 instance->m_lOffset.QuadPart = newoffset;
307
308 if (plibNewPosition)
309 plibNewPosition->QuadPart = instance->m_lOffset.QuadPart;
310
311 return S_OK;
312}
313
314static HRESULT STDMETHODCALLTYPE CliprdrStream_SetSize(IStream* This, ULARGE_INTEGER libNewSize)
315{
316 (void)This;
317 (void)libNewSize;
318 return E_NOTIMPL;
319}
320
321static HRESULT STDMETHODCALLTYPE CliprdrStream_CopyTo(IStream* This, IStream* pstm,
322 ULARGE_INTEGER cb, ULARGE_INTEGER* pcbRead,
323 ULARGE_INTEGER* pcbWritten)
324{
325 (void)This;
326 (void)pstm;
327 (void)cb;
328 (void)pcbRead;
329 (void)pcbWritten;
330 return E_NOTIMPL;
331}
332
333static HRESULT STDMETHODCALLTYPE CliprdrStream_Commit(IStream* This, DWORD grfCommitFlags)
334{
335 (void)This;
336 (void)grfCommitFlags;
337 return E_NOTIMPL;
338}
339
340static HRESULT STDMETHODCALLTYPE CliprdrStream_Revert(IStream* This)
341{
342 (void)This;
343 return E_NOTIMPL;
344}
345
346static HRESULT STDMETHODCALLTYPE CliprdrStream_LockRegion(IStream* This, ULARGE_INTEGER libOffset,
347 ULARGE_INTEGER cb, DWORD dwLockType)
348{
349 (void)This;
350 (void)libOffset;
351 (void)cb;
352 (void)dwLockType;
353 return E_NOTIMPL;
354}
355
356static HRESULT STDMETHODCALLTYPE CliprdrStream_UnlockRegion(IStream* This, ULARGE_INTEGER libOffset,
357 ULARGE_INTEGER cb, DWORD dwLockType)
358{
359 (void)This;
360 (void)libOffset;
361 (void)cb;
362 (void)dwLockType;
363 return E_NOTIMPL;
364}
365
366static HRESULT STDMETHODCALLTYPE CliprdrStream_Stat(IStream* This, STATSTG* pstatstg,
367 DWORD grfStatFlag)
368{
369 CliprdrStream* instance = (CliprdrStream*)This;
370
371 if (!instance)
372 return E_INVALIDARG;
373
374 if (pstatstg == nullptr)
375 return STG_E_INVALIDPOINTER;
376
377 ZeroMemory(pstatstg, sizeof(STATSTG));
378
379 switch (grfStatFlag)
380 {
381 case STATFLAG_DEFAULT:
382 return STG_E_INSUFFICIENTMEMORY;
383
384 case STATFLAG_NONAME:
385 pstatstg->cbSize.QuadPart = instance->m_lSize.QuadPart;
386 pstatstg->grfLocksSupported = LOCK_EXCLUSIVE;
387 pstatstg->grfMode = GENERIC_READ;
388 pstatstg->grfStateBits = 0;
389 pstatstg->type = STGTY_STREAM;
390 break;
391
392 case STATFLAG_NOOPEN:
393 return STG_E_INVALIDFLAG;
394
395 default:
396 return STG_E_INVALIDFLAG;
397 }
398
399 return S_OK;
400}
401
402static HRESULT STDMETHODCALLTYPE CliprdrStream_Clone(IStream* This, IStream** ppstm)
403{
404 (void)This;
405 (void)ppstm;
406 return E_NOTIMPL;
407}
408
409static CliprdrStream* CliprdrStream_New(ULONG index, void* pData, const FILEDESCRIPTORW* dsc)
410{
411 IStream* iStream;
412 BOOL success = FALSE;
413 BOOL isDir = FALSE;
414 CliprdrStream* instance;
415 wfClipboard* clipboard = (wfClipboard*)pData;
416 instance = (CliprdrStream*)calloc(1, sizeof(CliprdrStream));
417
418 if (instance)
419 {
420 instance->m_Dsc = *dsc;
421 iStream = &instance->iStream;
422 iStream->lpVtbl = (IStreamVtbl*)calloc(1, sizeof(IStreamVtbl));
423
424 if (iStream->lpVtbl)
425 {
426 iStream->lpVtbl->QueryInterface = CliprdrStream_QueryInterface;
427 iStream->lpVtbl->AddRef = CliprdrStream_AddRef;
428 iStream->lpVtbl->Release = CliprdrStream_Release;
429 iStream->lpVtbl->Read = CliprdrStream_Read;
430 iStream->lpVtbl->Write = CliprdrStream_Write;
431 iStream->lpVtbl->Seek = CliprdrStream_Seek;
432 iStream->lpVtbl->SetSize = CliprdrStream_SetSize;
433 iStream->lpVtbl->CopyTo = CliprdrStream_CopyTo;
434 iStream->lpVtbl->Commit = CliprdrStream_Commit;
435 iStream->lpVtbl->Revert = CliprdrStream_Revert;
436 iStream->lpVtbl->LockRegion = CliprdrStream_LockRegion;
437 iStream->lpVtbl->UnlockRegion = CliprdrStream_UnlockRegion;
438 iStream->lpVtbl->Stat = CliprdrStream_Stat;
439 iStream->lpVtbl->Clone = CliprdrStream_Clone;
440 instance->m_lRefCount = 1;
441 instance->m_lIndex = index;
442 instance->m_pData = pData;
443 instance->m_lOffset.QuadPart = 0;
444
445 if (instance->m_Dsc.dwFlags & FD_ATTRIBUTES)
446 {
447 if (instance->m_Dsc.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
448 isDir = TRUE;
449 }
450
451 if (((instance->m_Dsc.dwFlags & FD_FILESIZE) == 0) && !isDir)
452 {
453 /* get content size of this stream */
454 if (cliprdr_send_request_filecontents(clipboard, (void*)instance,
455 instance->m_lIndex, FILECONTENTS_SIZE, 0,
456 8) == CHANNEL_RC_OK)
457 {
458 success = TRUE;
459 }
460
461 if (clipboard->req_fsize < 8)
462 success = FALSE;
463 else
464 instance->m_lSize.QuadPart = *((LONGLONG*)clipboard->req_fdata);
465 free(clipboard->req_fdata);
466 }
467 else
468 {
469 instance->m_lSize.QuadPart =
470 ((UINT64)instance->m_Dsc.nFileSizeHigh << 32) | instance->m_Dsc.nFileSizeLow;
471 success = TRUE;
472 }
473 }
474 }
475
476 if (!success)
477 {
478 CliprdrStream_Delete(instance);
479 instance = nullptr;
480 }
481
482 return instance;
483}
484
485void CliprdrStream_Delete(CliprdrStream* instance)
486{
487 if (instance)
488 {
489 free(instance->iStream.lpVtbl);
490 free(instance);
491 }
492}
493
498static LONG cliprdr_lookup_format(CliprdrDataObject* instance, FORMATETC* pFormatEtc)
499{
500 if (!instance || !pFormatEtc)
501 return -1;
502
503 for (ULONG i = 0; i < instance->m_nNumFormats; i++)
504 {
505 if ((pFormatEtc->tymed & instance->m_pFormatEtc[i].tymed) &&
506 pFormatEtc->cfFormat == instance->m_pFormatEtc[i].cfFormat &&
507 pFormatEtc->dwAspect & instance->m_pFormatEtc[i].dwAspect)
508 {
509 return (LONG)i;
510 }
511 }
512
513 return -1;
514}
515
516static HRESULT STDMETHODCALLTYPE CliprdrDataObject_QueryInterface(IDataObject* This, REFIID riid,
517 void** ppvObject)
518{
519 (void)This;
520
521 if (!ppvObject)
522 return E_INVALIDARG;
523
524 if (IsEqualIID(riid, &IID_IDataObject) || IsEqualIID(riid, &IID_IUnknown))
525 {
526 IDataObject_AddRef(This);
527 *ppvObject = This;
528 return S_OK;
529 }
530 else
531 {
532 *ppvObject = 0;
533 return E_NOINTERFACE;
534 }
535}
536
537static ULONG STDMETHODCALLTYPE CliprdrDataObject_AddRef(IDataObject* This)
538{
539 CliprdrDataObject* instance = (CliprdrDataObject*)This;
540
541 if (!instance)
542 return E_INVALIDARG;
543
544 return InterlockedIncrement(&instance->m_lRefCount);
545}
546
547static ULONG STDMETHODCALLTYPE CliprdrDataObject_Release(IDataObject* This)
548{
549 LONG count;
550 CliprdrDataObject* instance = (CliprdrDataObject*)This;
551
552 if (!instance)
553 return E_INVALIDARG;
554
555 count = InterlockedDecrement(&instance->m_lRefCount);
556
557 if (count == 0)
558 {
559 CliprdrDataObject_Delete(instance);
560 return 0;
561 }
562 else
563 return count;
564}
565
566static HRESULT STDMETHODCALLTYPE CliprdrDataObject_GetData(IDataObject* This, FORMATETC* pFormatEtc,
567 STGMEDIUM* pMedium)
568{
569 LONG idx;
570 CliprdrDataObject* instance = (CliprdrDataObject*)This;
571 wfClipboard* clipboard;
572
573 if (!pFormatEtc || !pMedium || !instance)
574 return E_INVALIDARG;
575
576 clipboard = (wfClipboard*)instance->m_pData;
577
578 if (!clipboard)
579 return E_INVALIDARG;
580
581 if ((idx = cliprdr_lookup_format(instance, pFormatEtc)) == -1)
582 return DV_E_FORMATETC;
583
584 pMedium->tymed = instance->m_pFormatEtc[idx].tymed;
585 pMedium->pUnkForRelease = 0;
586
587 if (instance->m_pFormatEtc[idx].cfFormat == RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW))
588 {
589 FILEGROUPDESCRIPTOR* dsc;
590 DWORD remote = get_remote_format_id(clipboard, instance->m_pFormatEtc[idx].cfFormat);
591
592 if (cliprdr_send_data_request(clipboard, remote) != 0)
593 return E_UNEXPECTED;
594
595 pMedium->u.hGlobal = clipboard->hmem;
596 /* points to a FILEGROUPDESCRIPTOR structure */
597 /* GlobalLock returns a pointer to the first byte of the memory block,
598 * in which is a FILEGROUPDESCRIPTOR structure, whose first UINT member
599 * is the number of FILEDESCRIPTOR's */
600 dsc = (FILEGROUPDESCRIPTOR*)GlobalLock(clipboard->hmem);
601 instance->m_nStreams = dsc->cItems;
602 GlobalUnlock(clipboard->hmem);
603
604 if (instance->m_nStreams > 0)
605 {
606 if (!instance->m_pStream)
607 {
608 instance->m_pStream = (LPSTREAM*)calloc(instance->m_nStreams, sizeof(LPSTREAM));
609
610 if (instance->m_pStream)
611 {
612 for (ULONG i = 0; i < instance->m_nStreams; i++)
613 {
614 instance->m_pStream[i] =
615 (IStream*)CliprdrStream_New(i, clipboard, &dsc->fgd[i]);
616
617 if (!instance->m_pStream[i])
618 return E_OUTOFMEMORY;
619 }
620 }
621 }
622 }
623
624 if (!instance->m_pStream)
625 {
626 if (clipboard->hmem)
627 {
628 GlobalFree(clipboard->hmem);
629 clipboard->hmem = nullptr;
630 }
631
632 pMedium->u.hGlobal = nullptr;
633 return E_OUTOFMEMORY;
634 }
635 }
636 else if (instance->m_pFormatEtc[idx].cfFormat == RegisterClipboardFormat(CFSTR_FILECONTENTS))
637 {
638 if ((pFormatEtc->lindex >= 0) && ((ULONG)pFormatEtc->lindex < instance->m_nStreams))
639 {
640 pMedium->u.pstm = instance->m_pStream[pFormatEtc->lindex];
641 IDataObject_AddRef(instance->m_pStream[pFormatEtc->lindex]);
642 }
643 else
644 return E_INVALIDARG;
645 }
646 else
647 return E_UNEXPECTED;
648
649 return S_OK;
650}
651
652static HRESULT STDMETHODCALLTYPE CliprdrDataObject_GetDataHere(IDataObject* This,
653 FORMATETC* pformatetc,
654 STGMEDIUM* pmedium)
655{
656 (void)This;
657 (void)pformatetc;
658 (void)pmedium;
659 return E_NOTIMPL;
660}
661
662static HRESULT STDMETHODCALLTYPE CliprdrDataObject_QueryGetData(IDataObject* This,
663 FORMATETC* pformatetc)
664{
665 CliprdrDataObject* instance = (CliprdrDataObject*)This;
666
667 if (!pformatetc)
668 return E_INVALIDARG;
669
670 if (cliprdr_lookup_format(instance, pformatetc) == -1)
671 return DV_E_FORMATETC;
672
673 return S_OK;
674}
675
676static HRESULT STDMETHODCALLTYPE CliprdrDataObject_GetCanonicalFormatEtc(IDataObject* This,
677 FORMATETC* pformatectIn,
678 FORMATETC* pformatetcOut)
679{
680 (void)This;
681 (void)pformatectIn;
682
683 if (!pformatetcOut)
684 return E_INVALIDARG;
685
686 pformatetcOut->ptd = nullptr;
687 return E_NOTIMPL;
688}
689
690static HRESULT STDMETHODCALLTYPE CliprdrDataObject_SetData(IDataObject* This, FORMATETC* pformatetc,
691 STGMEDIUM* pmedium, BOOL fRelease)
692{
693 (void)This;
694 (void)pformatetc;
695 (void)pmedium;
696 (void)fRelease;
697 return E_NOTIMPL;
698}
699
700static HRESULT STDMETHODCALLTYPE CliprdrDataObject_EnumFormatEtc(IDataObject* This,
701 DWORD dwDirection,
702 IEnumFORMATETC** ppenumFormatEtc)
703{
704 CliprdrDataObject* instance = (CliprdrDataObject*)This;
705
706 if (!instance || !ppenumFormatEtc)
707 return E_INVALIDARG;
708
709 if (dwDirection == DATADIR_GET)
710 {
711 *ppenumFormatEtc = (IEnumFORMATETC*)CliprdrEnumFORMATETC_New(instance->m_nNumFormats,
712 instance->m_pFormatEtc);
713 return (*ppenumFormatEtc) ? S_OK : E_OUTOFMEMORY;
714 }
715 else
716 {
717 return E_NOTIMPL;
718 }
719}
720
721static HRESULT STDMETHODCALLTYPE CliprdrDataObject_DAdvise(IDataObject* This, FORMATETC* pformatetc,
722 DWORD advf, IAdviseSink* pAdvSink,
723 DWORD* pdwConnection)
724{
725 (void)This;
726 (void)pformatetc;
727 (void)advf;
728 (void)pAdvSink;
729 (void)pdwConnection;
730 return OLE_E_ADVISENOTSUPPORTED;
731}
732
733static HRESULT STDMETHODCALLTYPE CliprdrDataObject_DUnadvise(IDataObject* This, DWORD dwConnection)
734{
735 (void)This;
736 (void)dwConnection;
737 return OLE_E_ADVISENOTSUPPORTED;
738}
739
740static HRESULT STDMETHODCALLTYPE CliprdrDataObject_EnumDAdvise(IDataObject* This,
741 IEnumSTATDATA** ppenumAdvise)
742{
743 (void)This;
744 (void)ppenumAdvise;
745 return OLE_E_ADVISENOTSUPPORTED;
746}
747
748static CliprdrDataObject* CliprdrDataObject_New(FORMATETC* fmtetc, STGMEDIUM* stgmed, ULONG count,
749 void* data)
750{
751 CliprdrDataObject* instance;
752 IDataObject* iDataObject;
753 instance = (CliprdrDataObject*)calloc(1, sizeof(CliprdrDataObject));
754
755 if (!instance)
756 goto error;
757
758 iDataObject = &instance->iDataObject;
759 iDataObject->lpVtbl = (IDataObjectVtbl*)calloc(1, sizeof(IDataObjectVtbl));
760
761 if (!iDataObject->lpVtbl)
762 goto error;
763
764 iDataObject->lpVtbl->QueryInterface = CliprdrDataObject_QueryInterface;
765 iDataObject->lpVtbl->AddRef = CliprdrDataObject_AddRef;
766 iDataObject->lpVtbl->Release = CliprdrDataObject_Release;
767 iDataObject->lpVtbl->GetData = CliprdrDataObject_GetData;
768 iDataObject->lpVtbl->GetDataHere = CliprdrDataObject_GetDataHere;
769 iDataObject->lpVtbl->QueryGetData = CliprdrDataObject_QueryGetData;
770 iDataObject->lpVtbl->GetCanonicalFormatEtc = CliprdrDataObject_GetCanonicalFormatEtc;
771 iDataObject->lpVtbl->SetData = CliprdrDataObject_SetData;
772 iDataObject->lpVtbl->EnumFormatEtc = CliprdrDataObject_EnumFormatEtc;
773 iDataObject->lpVtbl->DAdvise = CliprdrDataObject_DAdvise;
774 iDataObject->lpVtbl->DUnadvise = CliprdrDataObject_DUnadvise;
775 iDataObject->lpVtbl->EnumDAdvise = CliprdrDataObject_EnumDAdvise;
776 instance->m_lRefCount = 1;
777 instance->m_nNumFormats = count;
778 instance->m_pData = data;
779 instance->m_nStreams = 0;
780 instance->m_pStream = nullptr;
781
782 if (count > 0)
783 {
784 instance->m_pFormatEtc = (FORMATETC*)calloc(count, sizeof(FORMATETC));
785
786 if (!instance->m_pFormatEtc)
787 goto error;
788
789 instance->m_pStgMedium = (STGMEDIUM*)calloc(count, sizeof(STGMEDIUM));
790
791 if (!instance->m_pStgMedium)
792 goto error;
793
794 for (ULONG i = 0; i < count; i++)
795 {
796 instance->m_pFormatEtc[i] = fmtetc[i];
797 instance->m_pStgMedium[i] = stgmed[i];
798 }
799 }
800
801 return instance;
802error:
803 CliprdrDataObject_Delete(instance);
804 return nullptr;
805}
806
807void CliprdrDataObject_Delete(CliprdrDataObject* instance)
808{
809 if (instance)
810 {
811 free(instance->iDataObject.lpVtbl);
812 free(instance->m_pFormatEtc);
813 free(instance->m_pStgMedium);
814
815 if (instance->m_pStream)
816 {
817 for (ULONG i = 0; i < instance->m_nStreams; i++)
818 CliprdrStream_Release(instance->m_pStream[i]);
819
820 free(instance->m_pStream);
821 }
822
823 free(instance);
824 }
825}
826
827static BOOL wf_create_file_obj(wfClipboard* clipboard, IDataObject** ppDataObject)
828{
829 FORMATETC fmtetc[2] = WINPR_C_ARRAY_INIT;
830 STGMEDIUM stgmeds[2] = WINPR_C_ARRAY_INIT;
831
832 if (!ppDataObject)
833 return FALSE;
834
835 fmtetc[0].cfFormat = RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW);
836 fmtetc[0].dwAspect = DVASPECT_CONTENT;
837
838 fmtetc[0].tymed = TYMED_HGLOBAL;
839 stgmeds[0].tymed = TYMED_HGLOBAL;
840
841 fmtetc[1].cfFormat = RegisterClipboardFormat(CFSTR_FILECONTENTS);
842 fmtetc[1].dwAspect = DVASPECT_CONTENT;
843
844 fmtetc[1].tymed = TYMED_ISTREAM;
845 stgmeds[1].tymed = TYMED_ISTREAM;
846
847 *ppDataObject = (IDataObject*)CliprdrDataObject_New(fmtetc, stgmeds, 2, clipboard);
848 return (*ppDataObject) ? TRUE : FALSE;
849}
850
851static void wf_destroy_file_obj(IDataObject* instance)
852{
853 if (instance)
854 IDataObject_Release(instance);
855}
856
861static void cliprdr_format_deep_copy(FORMATETC* dest, FORMATETC* source)
862{
863 *dest = *source;
864
865 if (source->ptd)
866 {
867 dest->ptd = (DVTARGETDEVICE*)CoTaskMemAlloc(sizeof(DVTARGETDEVICE));
868
869 if (dest->ptd)
870 *(dest->ptd) = *(source->ptd);
871 }
872}
873
874static HRESULT STDMETHODCALLTYPE CliprdrEnumFORMATETC_QueryInterface(IEnumFORMATETC* This,
875 REFIID riid, void** ppvObject)
876{
877 (void)This;
878
879 if (IsEqualIID(riid, &IID_IEnumFORMATETC) || IsEqualIID(riid, &IID_IUnknown))
880 {
881 IEnumFORMATETC_AddRef(This);
882 *ppvObject = This;
883 return S_OK;
884 }
885 else
886 {
887 *ppvObject = 0;
888 return E_NOINTERFACE;
889 }
890}
891
892static ULONG STDMETHODCALLTYPE CliprdrEnumFORMATETC_AddRef(IEnumFORMATETC* This)
893{
894 CliprdrEnumFORMATETC* instance = (CliprdrEnumFORMATETC*)This;
895
896 if (!instance)
897 return 0;
898
899 return InterlockedIncrement(&instance->m_lRefCount);
900}
901
902static ULONG STDMETHODCALLTYPE CliprdrEnumFORMATETC_Release(IEnumFORMATETC* This)
903{
904 LONG count;
905 CliprdrEnumFORMATETC* instance = (CliprdrEnumFORMATETC*)This;
906
907 if (!instance)
908 return 0;
909
910 count = InterlockedDecrement(&instance->m_lRefCount);
911
912 if (count == 0)
913 {
914 CliprdrEnumFORMATETC_Delete(instance);
915 return 0;
916 }
917 else
918 {
919 return count;
920 }
921}
922
923static HRESULT STDMETHODCALLTYPE CliprdrEnumFORMATETC_Next(IEnumFORMATETC* This, ULONG celt,
924 FORMATETC* rgelt, ULONG* pceltFetched)
925{
926 ULONG copied = 0;
927 CliprdrEnumFORMATETC* instance = (CliprdrEnumFORMATETC*)This;
928
929 if (!instance || !celt || !rgelt)
930 return E_INVALIDARG;
931
932 while ((instance->m_nIndex < instance->m_nNumFormats) && (copied < celt))
933 {
934 cliprdr_format_deep_copy(&rgelt[copied++], &instance->m_pFormatEtc[instance->m_nIndex++]);
935 }
936
937 if (pceltFetched != 0)
938 *pceltFetched = copied;
939
940 return (copied == celt) ? S_OK : E_FAIL;
941}
942
943static HRESULT STDMETHODCALLTYPE CliprdrEnumFORMATETC_Skip(IEnumFORMATETC* This, ULONG celt)
944{
945 CliprdrEnumFORMATETC* instance = (CliprdrEnumFORMATETC*)This;
946
947 if (!instance)
948 return E_INVALIDARG;
949
950 if (instance->m_nIndex + (LONG)celt > instance->m_nNumFormats)
951 return E_FAIL;
952
953 instance->m_nIndex += celt;
954 return S_OK;
955}
956
957static HRESULT STDMETHODCALLTYPE CliprdrEnumFORMATETC_Reset(IEnumFORMATETC* This)
958{
959 CliprdrEnumFORMATETC* instance = (CliprdrEnumFORMATETC*)This;
960
961 if (!instance)
962 return E_INVALIDARG;
963
964 instance->m_nIndex = 0;
965 return S_OK;
966}
967
968static HRESULT STDMETHODCALLTYPE CliprdrEnumFORMATETC_Clone(IEnumFORMATETC* This,
969 IEnumFORMATETC** ppEnum)
970{
971 CliprdrEnumFORMATETC* instance = (CliprdrEnumFORMATETC*)This;
972
973 if (!instance || !ppEnum)
974 return E_INVALIDARG;
975
976 *ppEnum =
977 (IEnumFORMATETC*)CliprdrEnumFORMATETC_New(instance->m_nNumFormats, instance->m_pFormatEtc);
978
979 if (!*ppEnum)
980 return E_OUTOFMEMORY;
981
982 ((CliprdrEnumFORMATETC*)*ppEnum)->m_nIndex = instance->m_nIndex;
983 return S_OK;
984}
985
986CliprdrEnumFORMATETC* CliprdrEnumFORMATETC_New(ULONG nFormats, FORMATETC* pFormatEtc)
987{
988 CliprdrEnumFORMATETC* instance;
989 IEnumFORMATETC* iEnumFORMATETC;
990
991 if ((nFormats != 0) && !pFormatEtc)
992 return nullptr;
993
994 instance = (CliprdrEnumFORMATETC*)calloc(1, sizeof(CliprdrEnumFORMATETC));
995
996 if (!instance)
997 goto error;
998
999 iEnumFORMATETC = &instance->iEnumFORMATETC;
1000 iEnumFORMATETC->lpVtbl = (IEnumFORMATETCVtbl*)calloc(1, sizeof(IEnumFORMATETCVtbl));
1001
1002 if (!iEnumFORMATETC->lpVtbl)
1003 goto error;
1004
1005 iEnumFORMATETC->lpVtbl->QueryInterface = CliprdrEnumFORMATETC_QueryInterface;
1006 iEnumFORMATETC->lpVtbl->AddRef = CliprdrEnumFORMATETC_AddRef;
1007 iEnumFORMATETC->lpVtbl->Release = CliprdrEnumFORMATETC_Release;
1008 iEnumFORMATETC->lpVtbl->Next = CliprdrEnumFORMATETC_Next;
1009 iEnumFORMATETC->lpVtbl->Skip = CliprdrEnumFORMATETC_Skip;
1010 iEnumFORMATETC->lpVtbl->Reset = CliprdrEnumFORMATETC_Reset;
1011 iEnumFORMATETC->lpVtbl->Clone = CliprdrEnumFORMATETC_Clone;
1012 instance->m_lRefCount = 1;
1013 instance->m_nIndex = 0;
1014 instance->m_nNumFormats = nFormats;
1015
1016 if (nFormats > 0)
1017 {
1018 instance->m_pFormatEtc = (FORMATETC*)calloc(nFormats, sizeof(FORMATETC));
1019
1020 if (!instance->m_pFormatEtc)
1021 goto error;
1022
1023 for (ULONG i = 0; i < nFormats; i++)
1024 cliprdr_format_deep_copy(&instance->m_pFormatEtc[i], &pFormatEtc[i]);
1025 }
1026
1027 return instance;
1028error:
1029 CliprdrEnumFORMATETC_Delete(instance);
1030 return nullptr;
1031}
1032
1033void CliprdrEnumFORMATETC_Delete(CliprdrEnumFORMATETC* instance)
1034{
1035 if (instance)
1036 {
1037 free(instance->iEnumFORMATETC.lpVtbl);
1038
1039 if (instance->m_pFormatEtc)
1040 {
1041 for (LONG i = 0; i < instance->m_nNumFormats; i++)
1042 {
1043 if (instance->m_pFormatEtc[i].ptd)
1044 CoTaskMemFree(instance->m_pFormatEtc[i].ptd);
1045 }
1046
1047 free(instance->m_pFormatEtc);
1048 }
1049
1050 free(instance);
1051 }
1052}
1053
1054/***********************************************************************************/
1055
1056static UINT32 get_local_format_id_by_name(wfClipboard* clipboard, const TCHAR* format_name)
1057{
1058 formatMapping* map;
1059 WCHAR* unicode_name;
1060#if !defined(UNICODE)
1061 size_t size;
1062#endif
1063
1064 if (!clipboard || !format_name)
1065 return 0;
1066
1067#if defined(UNICODE)
1068 unicode_name = _wcsdup(format_name);
1069#else
1070 size = _tcslen(format_name);
1071 unicode_name = calloc(size + 1, sizeof(WCHAR));
1072
1073 if (!unicode_name)
1074 return 0;
1075
1076 MultiByteToWideChar(CP_OEMCP, 0, format_name, strlen(format_name), unicode_name, size);
1077#endif
1078
1079 if (!unicode_name)
1080 return 0;
1081
1082 for (size_t i = 0; i < clipboard->map_size; i++)
1083 {
1084 map = &clipboard->format_mappings[i];
1085
1086 if (map->name)
1087 {
1088 if (wcscmp(map->name, unicode_name) == 0)
1089 {
1090 free(unicode_name);
1091 return map->local_format_id;
1092 }
1093 }
1094 }
1095
1096 free(unicode_name);
1097 return 0;
1098}
1099
1100static inline BOOL file_transferring(wfClipboard* clipboard)
1101{
1102 return get_local_format_id_by_name(clipboard, CFSTR_FILEDESCRIPTORW) ? TRUE : FALSE;
1103}
1104
1105static UINT32 get_remote_format_id(wfClipboard* clipboard, UINT32 local_format)
1106{
1107 formatMapping* map;
1108
1109 if (!clipboard)
1110 return 0;
1111
1112 for (UINT32 i = 0; i < clipboard->map_size; i++)
1113 {
1114 map = &clipboard->format_mappings[i];
1115
1116 if (map->local_format_id == local_format)
1117 return map->remote_format_id;
1118 }
1119
1120 return local_format;
1121}
1122
1123static BOOL map_ensure_capacity(wfClipboard* clipboard)
1124{
1125 if (!clipboard)
1126 return FALSE;
1127
1128 if (clipboard->map_size >= clipboard->map_capacity)
1129 {
1130 size_t new_size = clipboard->map_capacity;
1131 do
1132 {
1133 if (new_size > (SIZE_MAX - 128ull) / sizeof(formatMapping))
1134 return FALSE;
1135 new_size += 128ull;
1136 } while (new_size <= clipboard->map_size);
1137
1138 const size_t diff = new_size - clipboard->map_size;
1139 formatMapping* new_map =
1140 (formatMapping*)realloc(clipboard->format_mappings, sizeof(formatMapping) * new_size);
1141
1142 if (!new_map)
1143 return FALSE;
1144
1145 memset(&new_map[clipboard->map_size], 0, sizeof(formatMapping) * diff);
1146 clipboard->format_mappings = new_map;
1147 clipboard->map_capacity = new_size;
1148 }
1149 return TRUE;
1150}
1151
1152static BOOL clear_format_map(wfClipboard* clipboard)
1153{
1154 formatMapping* map;
1155
1156 if (!clipboard)
1157 return FALSE;
1158
1159 if (clipboard->format_mappings)
1160 {
1161 for (size_t i = 0; i < clipboard->map_capacity; i++)
1162 {
1163 map = &clipboard->format_mappings[i];
1164 map->remote_format_id = 0;
1165 map->local_format_id = 0;
1166 free(map->name);
1167 map->name = nullptr;
1168 }
1169 }
1170
1171 clipboard->map_size = 0;
1172 return TRUE;
1173}
1174
1175static UINT cliprdr_send_tempdir(wfClipboard* clipboard)
1176{
1177 CLIPRDR_TEMP_DIRECTORY tempDirectory;
1178
1179 if (!clipboard)
1180 return -1;
1181
1182 if (GetEnvironmentVariableA("TEMP", tempDirectory.szTempDir, sizeof(tempDirectory.szTempDir)) ==
1183 0)
1184 return -1;
1185
1186 return clipboard->context->TempDirectory(clipboard->context, &tempDirectory);
1187}
1188
1189static BOOL cliprdr_GetUpdatedClipboardFormats(wfClipboard* clipboard, PUINT lpuiFormats,
1190 UINT cFormats, PUINT pcFormatsOut)
1191{
1192 UINT index = 0;
1193 UINT format = 0;
1194 BOOL clipboardOpen = FALSE;
1195
1196 if (!clipboard->legacyApi)
1197 return clipboard->GetUpdatedClipboardFormats(lpuiFormats, cFormats, pcFormatsOut);
1198
1199 clipboardOpen = try_open_clipboard(clipboard->hwnd);
1200
1201 if (!clipboardOpen)
1202 {
1203 *pcFormatsOut = 0;
1204 return TRUE; /* Other app holding clipboard */
1205 }
1206
1207 while (index < cFormats)
1208 {
1209 format = EnumClipboardFormats(format);
1210
1211 if (!format)
1212 break;
1213
1214 lpuiFormats[index] = format;
1215 index++;
1216 }
1217
1218 *pcFormatsOut = index;
1219 CloseClipboard();
1220 return TRUE;
1221}
1222
1223static UINT cliprdr_send_format_list(wfClipboard* clipboard)
1224{
1225 UINT rc;
1226 int count = 0;
1227 UINT32 numFormats = 0;
1228 UINT32 formatId = 0;
1229 char formatName[1024];
1230 CLIPRDR_FORMAT* formats = nullptr;
1231 CLIPRDR_FORMAT_LIST formatList = WINPR_C_ARRAY_INIT;
1232
1233 if (!clipboard)
1234 return ERROR_INTERNAL_ERROR;
1235
1236 /* Ignore if other app is holding clipboard */
1237 if (try_open_clipboard(clipboard->hwnd))
1238 {
1239 count = CountClipboardFormats();
1240 numFormats = (UINT32)count;
1241 formats = (CLIPRDR_FORMAT*)calloc(numFormats, sizeof(CLIPRDR_FORMAT));
1242
1243 if (!formats)
1244 {
1245 CloseClipboard();
1246 return CHANNEL_RC_NO_MEMORY;
1247 }
1248
1249 {
1250 UINT32 index = 0;
1251
1252 if (IsClipboardFormatAvailable(CF_HDROP))
1253 {
1254 formats[index++].formatId = RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW);
1255 formats[index++].formatId = RegisterClipboardFormat(CFSTR_FILECONTENTS);
1256 }
1257 else
1258 {
1259 while ((formatId = EnumClipboardFormats(formatId)) != 0)
1260 formats[index++].formatId = formatId;
1261 }
1262
1263 numFormats = index;
1264 }
1265
1266 if (!CloseClipboard())
1267 {
1268 free(formats);
1269 return ERROR_INTERNAL_ERROR;
1270 }
1271
1272 for (UINT index = 0; index < numFormats; index++)
1273 {
1274 if (GetClipboardFormatNameA(formats[index].formatId, formatName, sizeof(formatName)))
1275 {
1276 formats[index].formatName = _strdup(formatName);
1277 }
1278 }
1279 }
1280
1281 formatList.numFormats = numFormats;
1282 formatList.formats = formats;
1283 formatList.common.msgType = CB_FORMAT_LIST;
1284 rc = clipboard->context->ClientFormatList(clipboard->context, &formatList);
1285
1286 for (UINT index = 0; index < numFormats; index++)
1287 free(formats[index].formatName);
1288
1289 free(formats);
1290 return rc;
1291}
1292
1293static UINT cliprdr_send_data_request(wfClipboard* clipboard, UINT32 formatId)
1294{
1295 UINT rc;
1296 UINT32 remoteFormatId;
1297 CLIPRDR_FORMAT_DATA_REQUEST formatDataRequest;
1298
1299 if (!clipboard || !clipboard->context || !clipboard->context->ClientFormatDataRequest)
1300 return ERROR_INTERNAL_ERROR;
1301
1302 remoteFormatId = get_remote_format_id(clipboard, formatId);
1303
1304 formatDataRequest.requestedFormatId = remoteFormatId;
1305 clipboard->requestedFormatId = formatId;
1306 rc = clipboard->context->ClientFormatDataRequest(clipboard->context, &formatDataRequest);
1307
1308 if (WaitForSingleObject(clipboard->response_data_event, INFINITE) != WAIT_OBJECT_0)
1309 rc = ERROR_INTERNAL_ERROR;
1310 else if (!ResetEvent(clipboard->response_data_event))
1311 rc = ERROR_INTERNAL_ERROR;
1312
1313 return rc;
1314}
1315
1316UINT cliprdr_send_request_filecontents(wfClipboard* clipboard, const void* streamid, ULONG index,
1317 UINT32 flag, UINT64 position, ULONG nreq)
1318{
1319 UINT rc;
1320 CLIPRDR_FILE_CONTENTS_REQUEST fileContentsRequest;
1321
1322 if (!clipboard || !clipboard->context || !clipboard->context->ClientFileContentsRequest)
1323 return ERROR_INTERNAL_ERROR;
1324
1325 fileContentsRequest.streamId = (UINT32)(ULONG_PTR)streamid;
1326 fileContentsRequest.listIndex = index;
1327 fileContentsRequest.dwFlags = flag;
1328 fileContentsRequest.nPositionLow = position & 0xFFFFFFFF;
1329 fileContentsRequest.nPositionHigh = (position >> 32) & 0xFFFFFFFF;
1330 fileContentsRequest.cbRequested = nreq;
1331 fileContentsRequest.clipDataId = 0;
1332 fileContentsRequest.common.msgFlags = 0;
1333 rc = clipboard->context->ClientFileContentsRequest(clipboard->context, &fileContentsRequest);
1334
1335 if (WaitForSingleObject(clipboard->req_fevent, INFINITE) != WAIT_OBJECT_0)
1336 rc = ERROR_INTERNAL_ERROR;
1337 else if (!ResetEvent(clipboard->req_fevent))
1338 rc = ERROR_INTERNAL_ERROR;
1339
1340 if (nreq != clipboard->req_fsize)
1341 rc = ERROR_INVALID_DATA;
1342 return rc;
1343}
1344
1345static UINT cliprdr_send_response_filecontents(wfClipboard* clipboard, UINT32 streamId, UINT32 size,
1346 BYTE* data)
1347{
1348 CLIPRDR_FILE_CONTENTS_RESPONSE fileContentsResponse = WINPR_C_ARRAY_INIT;
1349
1350 if (!clipboard || !clipboard->context || !clipboard->context->ClientFileContentsResponse)
1351 return ERROR_INTERNAL_ERROR;
1352
1353 fileContentsResponse.streamId = streamId;
1354 fileContentsResponse.cbRequested = size;
1355 fileContentsResponse.requestedData = data;
1356 fileContentsResponse.common.msgFlags = CB_RESPONSE_OK;
1357 return clipboard->context->ClientFileContentsResponse(clipboard->context,
1358 &fileContentsResponse);
1359}
1360
1361static LRESULT CALLBACK cliprdr_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
1362{
1363 static wfClipboard* clipboard = nullptr;
1364
1365 switch (Msg)
1366 {
1367 case WM_CREATE:
1368 DEBUG_CLIPRDR("info: WM_CREATE");
1369 clipboard = (wfClipboard*)((CREATESTRUCT*)lParam)->lpCreateParams;
1370 clipboard->hwnd = hWnd;
1371
1372 if (!clipboard->legacyApi)
1373 clipboard->AddClipboardFormatListener(hWnd);
1374 else
1375 clipboard->hWndNextViewer = SetClipboardViewer(hWnd);
1376
1377 break;
1378
1379 case WM_CLOSE:
1380 DEBUG_CLIPRDR("info: WM_CLOSE");
1381
1382 if (!clipboard->legacyApi)
1383 clipboard->RemoveClipboardFormatListener(hWnd);
1384
1385 break;
1386
1387 case WM_DESTROY:
1388 if (clipboard->legacyApi)
1389 ChangeClipboardChain(hWnd, clipboard->hWndNextViewer);
1390
1391 break;
1392
1393 case WM_CLIPBOARDUPDATE:
1394 DEBUG_CLIPRDR("info: WM_CLIPBOARDUPDATE");
1395
1396 if (clipboard->sync)
1397 {
1398 if ((GetClipboardOwner() != clipboard->hwnd) &&
1399 (S_FALSE == OleIsCurrentClipboard(clipboard->data_obj)))
1400 {
1401 if (clipboard->hmem)
1402 {
1403 GlobalFree(clipboard->hmem);
1404 clipboard->hmem = nullptr;
1405 }
1406
1407 cliprdr_send_format_list(clipboard);
1408 }
1409 }
1410
1411 break;
1412
1413 case WM_RENDERALLFORMATS:
1414 DEBUG_CLIPRDR("info: WM_RENDERALLFORMATS");
1415
1416 /* discard all contexts in clipboard */
1417 if (!try_open_clipboard(clipboard->hwnd))
1418 {
1419 DEBUG_CLIPRDR("OpenClipboard failed with 0x%x", GetLastError());
1420 break;
1421 }
1422
1423 EmptyClipboard();
1424 CloseClipboard();
1425 break;
1426
1427 case WM_RENDERFORMAT:
1428 DEBUG_CLIPRDR("info: WM_RENDERFORMAT");
1429
1430 if (cliprdr_send_data_request(clipboard, (UINT32)wParam) != 0)
1431 {
1432 DEBUG_CLIPRDR("error: cliprdr_send_data_request failed.");
1433 break;
1434 }
1435
1436 if (!SetClipboardData((UINT)wParam, clipboard->hmem))
1437 {
1438 DEBUG_CLIPRDR("SetClipboardData failed with 0x%x", GetLastError());
1439
1440 if (clipboard->hmem)
1441 {
1442 GlobalFree(clipboard->hmem);
1443 clipboard->hmem = nullptr;
1444 }
1445 }
1446
1447 /* Note: GlobalFree() is not needed when success */
1448 break;
1449
1450 case WM_DRAWCLIPBOARD:
1451 if (clipboard->legacyApi)
1452 {
1453 if ((GetClipboardOwner() != clipboard->hwnd) &&
1454 (S_FALSE == OleIsCurrentClipboard(clipboard->data_obj)))
1455 {
1456 cliprdr_send_format_list(clipboard);
1457 }
1458
1459 SendMessage(clipboard->hWndNextViewer, Msg, wParam, lParam);
1460 }
1461
1462 break;
1463
1464 case WM_CHANGECBCHAIN:
1465 if (clipboard->legacyApi)
1466 {
1467 HWND hWndCurrViewer = (HWND)wParam;
1468 HWND hWndNextViewer = (HWND)lParam;
1469
1470 if (hWndCurrViewer == clipboard->hWndNextViewer)
1471 clipboard->hWndNextViewer = hWndNextViewer;
1472 else if (clipboard->hWndNextViewer)
1473 SendMessage(clipboard->hWndNextViewer, Msg, wParam, lParam);
1474 }
1475
1476 break;
1477
1478 case WM_CLIPRDR_MESSAGE:
1479 DEBUG_CLIPRDR("info: WM_CLIPRDR_MESSAGE");
1480
1481 switch (wParam)
1482 {
1483 case OLE_SETCLIPBOARD:
1484 DEBUG_CLIPRDR("info: OLE_SETCLIPBOARD");
1485
1486 if (wf_create_file_obj(clipboard, &clipboard->data_obj))
1487 {
1488 if (OleSetClipboard(clipboard->data_obj) != S_OK)
1489 {
1490 wf_destroy_file_obj(clipboard->data_obj);
1491 clipboard->data_obj = nullptr;
1492 }
1493 }
1494
1495 break;
1496
1497 default:
1498 break;
1499 }
1500
1501 break;
1502
1503 case WM_DESTROYCLIPBOARD:
1504 case WM_ASKCBFORMATNAME:
1505 case WM_HSCROLLCLIPBOARD:
1506 case WM_PAINTCLIPBOARD:
1507 case WM_SIZECLIPBOARD:
1508 case WM_VSCROLLCLIPBOARD:
1509 default:
1510 return DefWindowProc(hWnd, Msg, wParam, lParam);
1511 }
1512
1513 return 0;
1514}
1515
1516static int create_cliprdr_window(wfClipboard* clipboard)
1517{
1518 WNDCLASSEX wnd_cls = WINPR_C_ARRAY_INIT;
1519
1520 wnd_cls.cbSize = sizeof(WNDCLASSEX);
1521 wnd_cls.style = CS_OWNDC;
1522 wnd_cls.lpfnWndProc = cliprdr_proc;
1523 wnd_cls.cbClsExtra = 0;
1524 wnd_cls.cbWndExtra = 0;
1525 wnd_cls.hIcon = nullptr;
1526 wnd_cls.hCursor = nullptr;
1527 wnd_cls.hbrBackground = nullptr;
1528 wnd_cls.lpszMenuName = nullptr;
1529 wnd_cls.lpszClassName = _T("ClipboardHiddenMessageProcessor");
1530 wnd_cls.hInstance = GetModuleHandle(nullptr);
1531 wnd_cls.hIconSm = nullptr;
1532 RegisterClassEx(&wnd_cls);
1533 clipboard->hwnd =
1534 CreateWindowEx(WS_EX_LEFT, _T("ClipboardHiddenMessageProcessor"), _T("rdpclip"), 0, 0, 0, 0,
1535 0, HWND_MESSAGE, nullptr, GetModuleHandle(nullptr), clipboard);
1536
1537 if (!clipboard->hwnd)
1538 {
1539 DEBUG_CLIPRDR("error: CreateWindowEx failed with %x.", GetLastError());
1540 return -1;
1541 }
1542
1543 return 0;
1544}
1545
1546static DWORD WINAPI cliprdr_thread_func(LPVOID arg)
1547{
1548 int ret;
1549 MSG msg;
1550 BOOL mcode;
1551 wfClipboard* clipboard = (wfClipboard*)arg;
1552 OleInitialize(0);
1553
1554 if ((ret = create_cliprdr_window(clipboard)) != 0)
1555 {
1556 OleUninitialize();
1557 DEBUG_CLIPRDR("error: create clipboard window failed.");
1558 return 0;
1559 }
1560
1561 while ((mcode = GetMessage(&msg, 0, 0, 0)) != 0)
1562 {
1563 if (mcode == -1)
1564 {
1565 DEBUG_CLIPRDR("error: clipboard thread GetMessage failed.");
1566 break;
1567 }
1568 else
1569 {
1570 TranslateMessage(&msg);
1571 DispatchMessage(&msg);
1572 }
1573 }
1574
1575 OleUninitialize();
1576 return 0;
1577}
1578
1579static void clear_file_array(wfClipboard* clipboard)
1580{
1581 if (!clipboard)
1582 return;
1583
1584 /* clear file_names array */
1585 if (clipboard->file_names)
1586 {
1587 for (size_t i = 0; i < clipboard->nFiles; i++)
1588 {
1589 free(clipboard->file_names[i]);
1590 clipboard->file_names[i] = nullptr;
1591 }
1592
1593 free(clipboard->file_names);
1594 clipboard->file_names = nullptr;
1595 }
1596
1597 /* clear fileDescriptor array */
1598 if (clipboard->fileDescriptor)
1599 {
1600 for (size_t i = 0; i < clipboard->nFiles; i++)
1601 {
1602 free(clipboard->fileDescriptor[i]);
1603 clipboard->fileDescriptor[i] = nullptr;
1604 }
1605
1606 free(clipboard->fileDescriptor);
1607 clipboard->fileDescriptor = nullptr;
1608 }
1609
1610 clipboard->file_array_size = 0;
1611 clipboard->nFiles = 0;
1612}
1613
1614static BOOL wf_cliprdr_get_file_contents(WCHAR* file_name, BYTE* buffer, LONG positionLow,
1615 LONG positionHigh, DWORD nRequested, DWORD* puSize)
1616{
1617 BOOL res = FALSE;
1618 HANDLE hFile;
1619 DWORD nGet, rc;
1620
1621 if (!file_name || !buffer || !puSize)
1622 {
1623 WLog_ERR(TAG, "get file contents Invalid Arguments.");
1624 return FALSE;
1625 }
1626
1627 hFile = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
1628 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1629
1630 if (hFile == INVALID_HANDLE_VALUE)
1631 return FALSE;
1632
1633 rc = SetFilePointer(hFile, positionLow, &positionHigh, FILE_BEGIN);
1634
1635 if (rc == INVALID_SET_FILE_POINTER)
1636 goto error;
1637
1638 if (!ReadFile(hFile, buffer, nRequested, &nGet, nullptr))
1639 {
1640 DEBUG_CLIPRDR("ReadFile failed with 0x%08lX.", GetLastError());
1641 goto error;
1642 }
1643
1644 res = TRUE;
1645error:
1646
1647 if (!CloseHandle(hFile))
1648 res = FALSE;
1649
1650 if (res)
1651 *puSize = nGet;
1652
1653 return res;
1654}
1655
1656/* path_name has a '\' at the end. e.g. c:\newfolder\, file_name is c:\newfolder\new.txt */
1657static FILEDESCRIPTORW* wf_cliprdr_get_file_descriptor(WCHAR* file_name, size_t pathLen)
1658{
1659 HANDLE hFile;
1660 FILEDESCRIPTORW* fd;
1661 fd = (FILEDESCRIPTORW*)calloc(1, sizeof(FILEDESCRIPTORW));
1662
1663 if (!fd)
1664 return nullptr;
1665
1666 hFile = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
1667 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1668
1669 if (hFile == INVALID_HANDLE_VALUE)
1670 {
1671 free(fd);
1672 return nullptr;
1673 }
1674
1675 fd->dwFlags = FD_ATTRIBUTES | FD_FILESIZE | FD_WRITESTIME | FD_PROGRESSUI;
1676 fd->dwFileAttributes = GetFileAttributes(file_name);
1677
1678 if (!GetFileTime(hFile, nullptr, nullptr, &fd->ftLastWriteTime))
1679 {
1680 fd->dwFlags &= ~FD_WRITESTIME;
1681 }
1682
1683 fd->nFileSizeLow = GetFileSize(hFile, &fd->nFileSizeHigh);
1684 wcscpy_s(fd->cFileName, sizeof(fd->cFileName) / 2, file_name + pathLen);
1685 (void)CloseHandle(hFile);
1686 return fd;
1687}
1688
1689static BOOL wf_cliprdr_array_ensure_capacity(wfClipboard* clipboard)
1690{
1691 if (!clipboard)
1692 return FALSE;
1693
1694 if (clipboard->nFiles == clipboard->file_array_size)
1695 {
1696 size_t new_size;
1697 FILEDESCRIPTORW** new_fd;
1698 WCHAR** new_name;
1699 new_size = (clipboard->file_array_size + 1) * 2;
1700 new_fd = (FILEDESCRIPTORW**)realloc(clipboard->fileDescriptor,
1701 new_size * sizeof(FILEDESCRIPTORW*));
1702
1703 if (new_fd)
1704 clipboard->fileDescriptor = new_fd;
1705
1706 new_name = (WCHAR**)realloc(clipboard->file_names, new_size * sizeof(WCHAR*));
1707
1708 if (new_name)
1709 clipboard->file_names = new_name;
1710
1711 if (!new_fd || !new_name)
1712 return FALSE;
1713
1714 clipboard->file_array_size = new_size;
1715 }
1716
1717 return TRUE;
1718}
1719
1720static BOOL wf_cliprdr_add_to_file_arrays(wfClipboard* clipboard, WCHAR* full_file_name,
1721 size_t pathLen)
1722{
1723 if (!wf_cliprdr_array_ensure_capacity(clipboard))
1724 return FALSE;
1725
1726 /* add to name array */
1727 clipboard->file_names[clipboard->nFiles] = (LPWSTR)malloc(MAX_PATH * 2);
1728
1729 if (!clipboard->file_names[clipboard->nFiles])
1730 return FALSE;
1731
1732 wcscpy_s(clipboard->file_names[clipboard->nFiles], MAX_PATH, full_file_name);
1733 /* add to descriptor array */
1734 clipboard->fileDescriptor[clipboard->nFiles] =
1735 wf_cliprdr_get_file_descriptor(full_file_name, pathLen);
1736
1737 if (!clipboard->fileDescriptor[clipboard->nFiles])
1738 {
1739 free(clipboard->file_names[clipboard->nFiles]);
1740 return FALSE;
1741 }
1742
1743 clipboard->nFiles++;
1744 return TRUE;
1745}
1746
1747static BOOL wf_cliprdr_traverse_directory(wfClipboard* clipboard, WCHAR* Dir, size_t pathLen)
1748{
1749 HANDLE hFind;
1750 WCHAR DirSpec[MAX_PATH];
1751 WIN32_FIND_DATA FindFileData;
1752
1753 if (!clipboard || !Dir)
1754 return FALSE;
1755
1756 StringCchCopy(DirSpec, MAX_PATH, Dir);
1757 StringCchCat(DirSpec, MAX_PATH, TEXT("\\*"));
1758 hFind = FindFirstFile(DirSpec, &FindFileData);
1759
1760 if (hFind == INVALID_HANDLE_VALUE)
1761 {
1762 DEBUG_CLIPRDR("FindFirstFile failed with 0x%x.", GetLastError());
1763 return FALSE;
1764 }
1765
1766 while (FindNextFile(hFind, &FindFileData))
1767 {
1768 if ((((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) &&
1769 (wcscmp(FindFileData.cFileName, _T(".")) == 0)) ||
1770 (wcscmp(FindFileData.cFileName, _T("..")) == 0))
1771 {
1772 continue;
1773 }
1774
1775 if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
1776 {
1777 WCHAR DirAdd[MAX_PATH];
1778 StringCchCopy(DirAdd, MAX_PATH, Dir);
1779 StringCchCat(DirAdd, MAX_PATH, _T("\\"));
1780 StringCchCat(DirAdd, MAX_PATH, FindFileData.cFileName);
1781
1782 if (!wf_cliprdr_add_to_file_arrays(clipboard, DirAdd, pathLen))
1783 return FALSE;
1784
1785 if (!wf_cliprdr_traverse_directory(clipboard, DirAdd, pathLen))
1786 return FALSE;
1787 }
1788 else
1789 {
1790 WCHAR fileName[MAX_PATH];
1791 StringCchCopy(fileName, MAX_PATH, Dir);
1792 StringCchCat(fileName, MAX_PATH, _T("\\"));
1793 StringCchCat(fileName, MAX_PATH, FindFileData.cFileName);
1794
1795 if (!wf_cliprdr_add_to_file_arrays(clipboard, fileName, pathLen))
1796 return FALSE;
1797 }
1798 }
1799
1800 FindClose(hFind);
1801 return TRUE;
1802}
1803
1804static UINT wf_cliprdr_send_client_capabilities(wfClipboard* clipboard)
1805{
1806 CLIPRDR_CAPABILITIES capabilities;
1807 CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet;
1808
1809 if (!clipboard || !clipboard->context || !clipboard->context->ClientCapabilities)
1810 return ERROR_INTERNAL_ERROR;
1811
1812 capabilities.cCapabilitiesSets = 1;
1813 capabilities.capabilitySets = (CLIPRDR_CAPABILITY_SET*)&(generalCapabilitySet);
1814 generalCapabilitySet.capabilitySetType = CB_CAPSTYPE_GENERAL;
1815 generalCapabilitySet.capabilitySetLength = 12;
1816 generalCapabilitySet.version = CB_CAPS_VERSION_2;
1817 generalCapabilitySet.generalFlags =
1818 CB_USE_LONG_FORMAT_NAMES | CB_STREAM_FILECLIP_ENABLED | CB_FILECLIP_NO_FILE_PATHS;
1819 return clipboard->context->ClientCapabilities(clipboard->context, &capabilities);
1820}
1821
1827static UINT wf_cliprdr_monitor_ready(CliprdrClientContext* context,
1828 const CLIPRDR_MONITOR_READY* monitorReady)
1829{
1830 UINT rc;
1831 wfClipboard* clipboard = (wfClipboard*)context->custom;
1832
1833 if (!context || !monitorReady)
1834 return ERROR_INTERNAL_ERROR;
1835
1836 clipboard->sync = TRUE;
1837 rc = wf_cliprdr_send_client_capabilities(clipboard);
1838
1839 if (rc != CHANNEL_RC_OK)
1840 return rc;
1841
1842 return cliprdr_send_format_list(clipboard);
1843}
1844
1850static UINT wf_cliprdr_server_capabilities(CliprdrClientContext* context,
1851 const CLIPRDR_CAPABILITIES* capabilities)
1852{
1853 CLIPRDR_CAPABILITY_SET* capabilitySet;
1854 wfClipboard* clipboard = (wfClipboard*)context->custom;
1855
1856 if (!context || !capabilities)
1857 return ERROR_INTERNAL_ERROR;
1858
1859 for (UINT32 index = 0; index < capabilities->cCapabilitiesSets; index++)
1860 {
1861 capabilitySet = &(capabilities->capabilitySets[index]);
1862
1863 if ((capabilitySet->capabilitySetType == CB_CAPSTYPE_GENERAL) &&
1864 (capabilitySet->capabilitySetLength >= CB_CAPSTYPE_GENERAL_LEN))
1865 {
1866 CLIPRDR_GENERAL_CAPABILITY_SET* generalCapabilitySet =
1867 (CLIPRDR_GENERAL_CAPABILITY_SET*)capabilitySet;
1868 clipboard->capabilities = generalCapabilitySet->generalFlags;
1869 break;
1870 }
1871 }
1872
1873 return CHANNEL_RC_OK;
1874}
1875
1881static UINT wf_cliprdr_server_format_list(CliprdrClientContext* context,
1882 const CLIPRDR_FORMAT_LIST* formatList)
1883{
1884 UINT rc = ERROR_INTERNAL_ERROR;
1885 wfClipboard* clipboard = (wfClipboard*)context->custom;
1886
1887 if (!clear_format_map(clipboard))
1888 return ERROR_INTERNAL_ERROR;
1889
1890 for (UINT32 i = 0; i < formatList->numFormats; i++)
1891 {
1892 if (!map_ensure_capacity(clipboard))
1893 return E_OUTOFMEMORY;
1894
1895 CLIPRDR_FORMAT* format = &(formatList->formats[i]);
1896 formatMapping* mapping = &(clipboard->format_mappings[i]);
1897 mapping->remote_format_id = format->formatId;
1898
1899 if (format->formatName)
1900 {
1901 mapping->name = ConvertUtf8ToWCharAlloc(format->formatName, nullptr);
1902
1903 if (mapping->name)
1904 mapping->local_format_id = RegisterClipboardFormatW((LPWSTR)mapping->name);
1905 }
1906 else
1907 {
1908 mapping->name = nullptr;
1909 mapping->local_format_id = mapping->remote_format_id;
1910 }
1911
1912 clipboard->map_size++;
1913 }
1914
1915 if (file_transferring(clipboard))
1916 {
1917 if (PostMessage(clipboard->hwnd, WM_CLIPRDR_MESSAGE, OLE_SETCLIPBOARD, 0))
1918 rc = CHANNEL_RC_OK;
1919 }
1920 else
1921 {
1922 if (!try_open_clipboard(clipboard->hwnd))
1923 return CHANNEL_RC_OK; /* Ignore, other app holding clipboard */
1924
1925 if (EmptyClipboard())
1926 {
1927 for (size_t i = 0; i < clipboard->map_size; i++)
1928 SetClipboardData(clipboard->format_mappings[i].local_format_id, nullptr);
1929
1930 rc = CHANNEL_RC_OK;
1931 }
1932
1933 if (!CloseClipboard() && GetLastError())
1934 return ERROR_INTERNAL_ERROR;
1935 }
1936
1937 return rc;
1938}
1939
1945static UINT
1946wf_cliprdr_server_format_list_response(CliprdrClientContext* context,
1947 const CLIPRDR_FORMAT_LIST_RESPONSE* formatListResponse)
1948{
1949 (void)context;
1950 (void)formatListResponse;
1951
1952 if (formatListResponse->common.msgFlags != CB_RESPONSE_OK)
1953 WLog_WARN(TAG, "format list update failed");
1954
1955 return CHANNEL_RC_OK;
1956}
1957
1963static UINT
1964wf_cliprdr_server_lock_clipboard_data(CliprdrClientContext* context,
1965 const CLIPRDR_LOCK_CLIPBOARD_DATA* lockClipboardData)
1966{
1967 (void)context;
1968 (void)lockClipboardData;
1969 return CHANNEL_RC_OK;
1970}
1971
1977static UINT
1978wf_cliprdr_server_unlock_clipboard_data(CliprdrClientContext* context,
1979 const CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData)
1980{
1981 (void)context;
1982 (void)unlockClipboardData;
1983 return CHANNEL_RC_OK;
1984}
1985
1986static BOOL wf_cliprdr_process_filename(wfClipboard* clipboard, WCHAR* wFileName, size_t str_len)
1987{
1988 size_t pathLen;
1989 size_t offset = str_len;
1990
1991 if (!clipboard || !wFileName)
1992 return FALSE;
1993
1994 /* find the last '\' in full file name */
1995 while (offset > 0)
1996 {
1997 if (wFileName[offset] == L'\\')
1998 break;
1999 else
2000 offset--;
2001 }
2002
2003 pathLen = offset + 1;
2004
2005 if (!wf_cliprdr_add_to_file_arrays(clipboard, wFileName, pathLen))
2006 return FALSE;
2007
2008 if ((clipboard->fileDescriptor[clipboard->nFiles - 1]->dwFileAttributes &
2009 FILE_ATTRIBUTE_DIRECTORY) != 0)
2010 {
2011 /* this is a directory */
2012 if (!wf_cliprdr_traverse_directory(clipboard, wFileName, pathLen))
2013 return FALSE;
2014 }
2015
2016 return TRUE;
2017}
2018
2019static SSIZE_T wf_cliprdr_tryopen(wfClipboard* clipboard, UINT32 requestedFormatId, BYTE** pData)
2020{
2021 SSIZE_T rc = -1;
2022 WINPR_ASSERT(clipboard);
2023 WINPR_ASSERT(pData);
2024
2025 *pData = nullptr;
2026
2027 /* Ignore if other app is holding the clipboard */
2028 if (!try_open_clipboard(clipboard->hwnd))
2029 return 0;
2030
2031 HANDLE hClipdata = GetClipboardData(requestedFormatId);
2032
2033 if (!hClipdata)
2034 goto fail;
2035
2036 char* globlemem = (char*)GlobalLock(hClipdata);
2037 const SSIZE_T size = GlobalSize(hClipdata);
2038 if (size <= 0)
2039 goto unlock;
2040
2041 BYTE* buff = malloc(size);
2042 if (buff == nullptr)
2043 goto fail;
2044 CopyMemory(buff, globlemem, size);
2045 *pData = buff;
2046 rc = size;
2047
2048unlock:
2049 GlobalUnlock(hClipdata);
2050
2051fail:
2052 CloseClipboard();
2053
2054 return rc;
2055}
2056
2057static SSIZE_T wf_cliprdr_get_filedescriptor(wfClipboard* clipboard, BYTE** pData)
2058{
2059 WINPR_ASSERT(clipboard);
2060 WINPR_ASSERT(pData);
2061
2062 SSIZE_T rc = -1;
2063 LPDATAOBJECT dataObj = nullptr;
2064 FORMATETC format_etc = WINPR_C_ARRAY_INIT;
2065 STGMEDIUM stg_medium = WINPR_C_ARRAY_INIT;
2066
2067 *pData = nullptr;
2068
2069 HRESULT result = OleGetClipboard(&dataObj);
2070 if (FAILED(result))
2071 return -1;
2072
2073 /* get DROPFILES struct from OLE */
2074 format_etc.cfFormat = CF_HDROP;
2075 format_etc.tymed = TYMED_HGLOBAL;
2076 format_etc.dwAspect = 1;
2077 format_etc.lindex = -1;
2078 result = IDataObject_GetData(dataObj, &format_etc, &stg_medium);
2079
2080 if (FAILED(result))
2081 {
2082 DEBUG_CLIPRDR("dataObj->GetData failed.");
2083 goto exit;
2084 }
2085
2086 HGLOBAL hdl = stg_medium.u.hGlobal;
2087 DROPFILES* dropFiles = (DROPFILES*)GlobalLock(hdl);
2088
2089 if (!dropFiles)
2090 {
2091 ReleaseStgMedium(&stg_medium);
2092 clipboard->nFiles = 0;
2093 goto exit;
2094 }
2095
2096 clear_file_array(clipboard);
2097
2098 if (dropFiles->fWide)
2099 {
2100 /* dropFiles contains file names */
2101 size_t len = 0;
2102 for (WCHAR* wFileName = (WCHAR*)((char*)dropFiles + dropFiles->pFiles);
2103 (len = wcslen(wFileName)) > 0; wFileName += len + 1)
2104 {
2105 wf_cliprdr_process_filename(clipboard, wFileName, wcslen(wFileName));
2106 }
2107 }
2108 else
2109 {
2110 size_t len = 0;
2111 for (char* p = (char*)((char*)dropFiles + dropFiles->pFiles); (len = strlen(p)) > 0;
2112 p += len + 1, clipboard->nFiles++)
2113 {
2114 const int ilen = WINPR_ASSERTING_INT_CAST(int, len);
2115 const int cchWideChar = MultiByteToWideChar(CP_ACP, MB_COMPOSITE, p, ilen, nullptr, 0);
2116 WCHAR* wFileName = (LPWSTR)calloc(cchWideChar, sizeof(WCHAR));
2117 MultiByteToWideChar(CP_ACP, MB_COMPOSITE, p, ilen, wFileName, cchWideChar);
2118 wf_cliprdr_process_filename(clipboard, wFileName, cchWideChar);
2119 free(wFileName);
2120 }
2121 }
2122
2123 GlobalUnlock(hdl);
2124 ReleaseStgMedium(&stg_medium);
2125exit:
2126{
2127 const size_t size = 4ull + clipboard->nFiles * sizeof(FILEDESCRIPTORW);
2128 FILEGROUPDESCRIPTORW* groupDsc = (FILEGROUPDESCRIPTORW*)calloc(size, 1);
2129
2130 if (groupDsc)
2131 {
2132 groupDsc->cItems = WINPR_ASSERTING_INT_CAST(UINT, clipboard->nFiles);
2133
2134 for (size_t i = 0; i < clipboard->nFiles; i++)
2135 {
2136 if (clipboard->fileDescriptor[i])
2137 groupDsc->fgd[i] = *clipboard->fileDescriptor[i];
2138 }
2139
2140 *pData = (BYTE*)groupDsc;
2141 rc = size;
2142 }
2143}
2144
2145 IDataObject_Release(dataObj);
2146 return rc;
2147}
2148
2154static UINT
2155wf_cliprdr_server_format_data_request(CliprdrClientContext* context,
2156 const CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest)
2157{
2158 if (!context || !formatDataRequest)
2159 return ERROR_INTERNAL_ERROR;
2160
2161 wfClipboard* clipboard = (wfClipboard*)context->custom;
2162
2163 if (!clipboard)
2164 return ERROR_INTERNAL_ERROR;
2165
2166 const UINT32 requestedFormatId = formatDataRequest->requestedFormatId;
2167 BYTE* requestedFormatData = nullptr;
2168 SSIZE_T res = 0;
2169 if (requestedFormatId == RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW))
2170 {
2171 res = wf_cliprdr_get_filedescriptor(clipboard, &requestedFormatData);
2172 }
2173 else
2174 {
2175 res = wf_cliprdr_tryopen(clipboard, requestedFormatId, &requestedFormatData);
2176 }
2177
2178 UINT rc = ERROR_INTERNAL_ERROR;
2179 if (res >= 0)
2180 {
2181 const CLIPRDR_FORMAT_DATA_RESPONSE response = {
2182 .common = { .msgType = CB_FORMAT_DATA_RESPONSE,
2183 .msgFlags = CB_RESPONSE_OK,
2184 .dataLen = (uint32_t)res },
2185 .requestedFormatData = requestedFormatData
2186 };
2187
2188 rc = clipboard->context->ClientFormatDataResponse(clipboard->context, &response);
2189 }
2190 else
2191 {
2192 const CLIPRDR_FORMAT_DATA_RESPONSE response = { .common = { .msgType =
2193 CB_FORMAT_DATA_RESPONSE,
2194 .msgFlags = CB_RESPONSE_FAIL,
2195 .dataLen = 0 },
2196 .requestedFormatData = nullptr };
2197
2198 rc = clipboard->context->ClientFormatDataResponse(clipboard->context, &response);
2199 }
2200
2201 free(requestedFormatData);
2202 return rc;
2203}
2204
2210static UINT
2211wf_cliprdr_server_format_data_response(CliprdrClientContext* context,
2212 const CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse)
2213{
2214 BYTE* data;
2215 HANDLE hMem;
2216 wfClipboard* clipboard;
2217
2218 if (!context || !formatDataResponse)
2219 return ERROR_INTERNAL_ERROR;
2220
2221 clipboard = (wfClipboard*)context->custom;
2222
2223 if (!clipboard)
2224 return ERROR_INTERNAL_ERROR;
2225
2226 if (formatDataResponse->common.msgFlags != CB_RESPONSE_OK)
2227 {
2228 clipboard->hmem = nullptr;
2229
2230 if (!SetEvent(clipboard->response_data_event))
2231 return ERROR_INTERNAL_ERROR;
2232
2233 return CHANNEL_RC_OK;
2234 }
2235
2236 hMem = GlobalAlloc(GMEM_MOVEABLE, formatDataResponse->common.dataLen);
2237
2238 if (!hMem)
2239 return ERROR_INTERNAL_ERROR;
2240
2241 data = (BYTE*)GlobalLock(hMem);
2242
2243 if (!data)
2244 {
2245 GlobalFree(hMem);
2246 return ERROR_INTERNAL_ERROR;
2247 }
2248
2249 CopyMemory(data, formatDataResponse->requestedFormatData, formatDataResponse->common.dataLen);
2250
2251 if (!GlobalUnlock(hMem) && GetLastError())
2252 {
2253 GlobalFree(hMem);
2254 return ERROR_INTERNAL_ERROR;
2255 }
2256
2257 clipboard->hmem = hMem;
2258
2259 if (!SetEvent(clipboard->response_data_event))
2260 return ERROR_INTERNAL_ERROR;
2261
2262 return CHANNEL_RC_OK;
2263}
2264
2270static UINT
2271wf_cliprdr_server_file_contents_request(CliprdrClientContext* context,
2272 const CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest)
2273{
2274 DWORD uSize = 0;
2275 BYTE* pData = nullptr;
2276 HRESULT hRet = S_OK;
2277 FORMATETC vFormatEtc = WINPR_C_ARRAY_INIT;
2278 LPDATAOBJECT pDataObj = nullptr;
2279 STGMEDIUM vStgMedium = WINPR_C_ARRAY_INIT;
2280 BOOL bIsStreamFile = TRUE;
2281 static LPSTREAM pStreamStc = nullptr;
2282 static UINT32 uStreamIdStc = 0;
2283 wfClipboard* clipboard;
2284 UINT rc = ERROR_INTERNAL_ERROR;
2285 UINT sRc;
2286 UINT32 cbRequested;
2287
2288 if (!context || !fileContentsRequest)
2289 return ERROR_INTERNAL_ERROR;
2290
2291 clipboard = (wfClipboard*)context->custom;
2292
2293 if (!clipboard)
2294 return ERROR_INTERNAL_ERROR;
2295
2296 cbRequested = fileContentsRequest->cbRequested;
2297 if (fileContentsRequest->dwFlags == FILECONTENTS_SIZE)
2298 cbRequested = sizeof(UINT64);
2299
2300 pData = (BYTE*)calloc(1, cbRequested);
2301
2302 if (!pData)
2303 goto error;
2304
2305 hRet = OleGetClipboard(&pDataObj);
2306
2307 if (FAILED(hRet))
2308 {
2309 WLog_ERR(TAG, "filecontents: get ole clipboard failed.");
2310 goto error;
2311 }
2312
2313 vFormatEtc.cfFormat = RegisterClipboardFormat(CFSTR_FILECONTENTS);
2314 vFormatEtc.tymed = TYMED_ISTREAM;
2315 vFormatEtc.dwAspect = 1;
2316 vFormatEtc.lindex = fileContentsRequest->listIndex;
2317 vFormatEtc.ptd = nullptr;
2318
2319 if ((uStreamIdStc != fileContentsRequest->streamId) || !pStreamStc)
2320 {
2321 LPENUMFORMATETC pEnumFormatEtc;
2322 ULONG CeltFetched;
2323 FORMATETC vFormatEtc2;
2324
2325 if (pStreamStc)
2326 {
2327 IStream_Release(pStreamStc);
2328 pStreamStc = nullptr;
2329 }
2330
2331 bIsStreamFile = FALSE;
2332 hRet = IDataObject_EnumFormatEtc(pDataObj, DATADIR_GET, &pEnumFormatEtc);
2333
2334 if (hRet == S_OK)
2335 {
2336 do
2337 {
2338 hRet = IEnumFORMATETC_Next(pEnumFormatEtc, 1, &vFormatEtc2, &CeltFetched);
2339
2340 if (hRet == S_OK)
2341 {
2342 if (vFormatEtc2.cfFormat == RegisterClipboardFormat(CFSTR_FILECONTENTS))
2343 {
2344 hRet = IDataObject_GetData(pDataObj, &vFormatEtc, &vStgMedium);
2345
2346 if (hRet == S_OK)
2347 {
2348 pStreamStc = vStgMedium.u.pstm;
2349 uStreamIdStc = fileContentsRequest->streamId;
2350 bIsStreamFile = TRUE;
2351 }
2352
2353 break;
2354 }
2355 }
2356 } while (hRet == S_OK);
2357 }
2358 }
2359
2360 if (bIsStreamFile == TRUE)
2361 {
2362 if (fileContentsRequest->dwFlags == FILECONTENTS_SIZE)
2363 {
2364 STATSTG vStatStg = WINPR_C_ARRAY_INIT;
2365 hRet = IStream_Stat(pStreamStc, &vStatStg, STATFLAG_NONAME);
2366
2367 if (hRet == S_OK)
2368 {
2369 *((UINT32*)&pData[0]) = vStatStg.cbSize.QuadPart & 0xFFFFFFFF;
2370 *((UINT32*)&pData[4]) = (vStatStg.cbSize.QuadPart >> 32) & 0xFFFFFFFF;
2371 uSize = cbRequested;
2372 }
2373 }
2374 else if (fileContentsRequest->dwFlags == FILECONTENTS_RANGE)
2375 {
2376 LARGE_INTEGER dlibMove;
2377 ULARGE_INTEGER dlibNewPosition;
2378 dlibMove.QuadPart = (INT64)(((UINT64)fileContentsRequest->nPositionHigh << 32) |
2379 fileContentsRequest->nPositionLow);
2380 hRet = IStream_Seek(pStreamStc, dlibMove, STREAM_SEEK_SET, &dlibNewPosition);
2381
2382 if (SUCCEEDED(hRet))
2383 hRet = IStream_Read(pStreamStc, pData, cbRequested, (PULONG)&uSize);
2384 }
2385 }
2386 else
2387 {
2388 if (fileContentsRequest->dwFlags == FILECONTENTS_SIZE)
2389 {
2390 if (clipboard->nFiles <= fileContentsRequest->listIndex)
2391 goto error;
2392 *((UINT32*)&pData[0]) =
2393 clipboard->fileDescriptor[fileContentsRequest->listIndex]->nFileSizeLow;
2394 *((UINT32*)&pData[4]) =
2395 clipboard->fileDescriptor[fileContentsRequest->listIndex]->nFileSizeHigh;
2396 uSize = cbRequested;
2397 }
2398 else if (fileContentsRequest->dwFlags == FILECONTENTS_RANGE)
2399 {
2400 BOOL bRet;
2401 if (clipboard->nFiles <= fileContentsRequest->listIndex)
2402 goto error;
2403 bRet = wf_cliprdr_get_file_contents(
2404 clipboard->file_names[fileContentsRequest->listIndex], pData,
2405 fileContentsRequest->nPositionLow, fileContentsRequest->nPositionHigh, cbRequested,
2406 &uSize);
2407
2408 if (bRet == FALSE)
2409 {
2410 WLog_ERR(TAG, "get file contents failed.");
2411 uSize = 0;
2412 goto error;
2413 }
2414 }
2415 }
2416
2417 rc = CHANNEL_RC_OK;
2418error:
2419
2420 if (pDataObj)
2421 IDataObject_Release(pDataObj);
2422
2423 if (uSize == 0)
2424 {
2425 free(pData);
2426 pData = nullptr;
2427 }
2428
2429 sRc =
2430 cliprdr_send_response_filecontents(clipboard, fileContentsRequest->streamId, uSize, pData);
2431 free(pData);
2432
2433 if (sRc != CHANNEL_RC_OK)
2434 return sRc;
2435
2436 return rc;
2437}
2438
2444static UINT
2445wf_cliprdr_server_file_contents_response(CliprdrClientContext* context,
2446 const CLIPRDR_FILE_CONTENTS_RESPONSE* fileContentsResponse)
2447{
2448 if (!context || !fileContentsResponse)
2449 return ERROR_INTERNAL_ERROR;
2450
2451 if (fileContentsResponse->common.msgFlags != CB_RESPONSE_OK)
2452 return E_FAIL;
2453
2454 wfClipboard* clipboard = (wfClipboard*)context->custom;
2455
2456 if (!clipboard)
2457 return ERROR_INTERNAL_ERROR;
2458
2459 clipboard->req_fsize = fileContentsResponse->cbRequested;
2460 clipboard->req_fdata = (char*)malloc(fileContentsResponse->cbRequested);
2461
2462 if (!clipboard->req_fdata)
2463 return ERROR_INTERNAL_ERROR;
2464
2465 CopyMemory(clipboard->req_fdata, fileContentsResponse->requestedData,
2466 fileContentsResponse->cbRequested);
2467
2468 if (!SetEvent(clipboard->req_fevent))
2469 {
2470 free(clipboard->req_fdata);
2471 return ERROR_INTERNAL_ERROR;
2472 }
2473
2474 return CHANNEL_RC_OK;
2475}
2476
2477BOOL wf_cliprdr_init(wfContext* wfc, CliprdrClientContext* cliprdr)
2478{
2479 wfClipboard* clipboard;
2480 rdpContext* context = (rdpContext*)wfc;
2481
2482 if (!context || !cliprdr)
2483 return FALSE;
2484
2485 wfc->clipboard = (wfClipboard*)calloc(1, sizeof(wfClipboard));
2486
2487 if (!wfc->clipboard)
2488 return FALSE;
2489
2490 clipboard = wfc->clipboard;
2491 clipboard->wfc = wfc;
2492 clipboard->context = cliprdr;
2493 clipboard->channels = context->channels;
2494 clipboard->sync = FALSE;
2495 clipboard->map_capacity = 32;
2496 clipboard->map_size = 0;
2497 clipboard->hUser32 = LoadLibraryA("user32.dll");
2498
2499 if (clipboard->hUser32)
2500 {
2501 clipboard->AddClipboardFormatListener = GetProcAddressAs(
2502 clipboard->hUser32, "AddClipboardFormatListener", fnAddClipboardFormatListener);
2503 clipboard->RemoveClipboardFormatListener = GetProcAddressAs(
2504 clipboard->hUser32, "RemoveClipboardFormatListener", fnRemoveClipboardFormatListener);
2505 clipboard->GetUpdatedClipboardFormats = GetProcAddressAs(
2506 clipboard->hUser32, "GetUpdatedClipboardFormats", fnGetUpdatedClipboardFormats);
2507 }
2508
2509 if (!(clipboard->hUser32 && clipboard->AddClipboardFormatListener &&
2510 clipboard->RemoveClipboardFormatListener && clipboard->GetUpdatedClipboardFormats))
2511 clipboard->legacyApi = TRUE;
2512
2513 if (!(clipboard->format_mappings =
2514 (formatMapping*)calloc(clipboard->map_capacity, sizeof(formatMapping))))
2515 goto error;
2516
2517 if (!(clipboard->response_data_event = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
2518 goto error;
2519
2520 if (!(clipboard->req_fevent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
2521 goto error;
2522
2523 if (!(clipboard->thread = CreateThread(nullptr, 0, cliprdr_thread_func, clipboard, 0, nullptr)))
2524 goto error;
2525
2526 cliprdr->MonitorReady = wf_cliprdr_monitor_ready;
2527 cliprdr->ServerCapabilities = wf_cliprdr_server_capabilities;
2528 cliprdr->ServerFormatList = wf_cliprdr_server_format_list;
2529 cliprdr->ServerFormatListResponse = wf_cliprdr_server_format_list_response;
2530 cliprdr->ServerLockClipboardData = wf_cliprdr_server_lock_clipboard_data;
2531 cliprdr->ServerUnlockClipboardData = wf_cliprdr_server_unlock_clipboard_data;
2532 cliprdr->ServerFormatDataRequest = wf_cliprdr_server_format_data_request;
2533 cliprdr->ServerFormatDataResponse = wf_cliprdr_server_format_data_response;
2534 cliprdr->ServerFileContentsRequest = wf_cliprdr_server_file_contents_request;
2535 cliprdr->ServerFileContentsResponse = wf_cliprdr_server_file_contents_response;
2536 cliprdr->custom = (void*)wfc->clipboard;
2537 return TRUE;
2538error:
2539 wf_cliprdr_uninit(wfc, cliprdr);
2540 return FALSE;
2541}
2542
2543BOOL wf_cliprdr_uninit(wfContext* wfc, CliprdrClientContext* cliprdr)
2544{
2545 wfClipboard* clipboard;
2546
2547 if (!wfc || !cliprdr)
2548 return FALSE;
2549
2550 clipboard = wfc->clipboard;
2551
2552 if (!clipboard)
2553 return FALSE;
2554
2555 cliprdr->custom = nullptr;
2556
2557 if (clipboard->hwnd)
2558 PostMessage(clipboard->hwnd, WM_QUIT, 0, 0);
2559
2560 if (clipboard->thread)
2561 {
2562 (void)WaitForSingleObject(clipboard->thread, INFINITE);
2563 (void)CloseHandle(clipboard->thread);
2564 }
2565
2566 if (clipboard->response_data_event)
2567 (void)CloseHandle(clipboard->response_data_event);
2568
2569 if (clipboard->req_fevent)
2570 (void)CloseHandle(clipboard->req_fevent);
2571
2572 clear_file_array(clipboard);
2573 clear_format_map(clipboard);
2574 free(clipboard->format_mappings);
2575 free(clipboard);
2576 return TRUE;
2577}