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