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