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 return rc;
1331}
1332
1333static UINT cliprdr_send_response_filecontents(wfClipboard* clipboard, UINT32 streamId, UINT32 size,
1334 BYTE* data)
1335{
1336 CLIPRDR_FILE_CONTENTS_RESPONSE fileContentsResponse;
1337
1338 if (!clipboard || !clipboard->context || !clipboard->context->ClientFileContentsResponse)
1339 return ERROR_INTERNAL_ERROR;
1340
1341 fileContentsResponse.streamId = streamId;
1342 fileContentsResponse.cbRequested = size;
1343 fileContentsResponse.requestedData = data;
1344 fileContentsResponse.common.msgFlags = CB_RESPONSE_OK;
1345 return clipboard->context->ClientFileContentsResponse(clipboard->context,
1346 &fileContentsResponse);
1347}
1348
1349static LRESULT CALLBACK cliprdr_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
1350{
1351 static wfClipboard* clipboard = nullptr;
1352
1353 switch (Msg)
1354 {
1355 case WM_CREATE:
1356 DEBUG_CLIPRDR("info: WM_CREATE");
1357 clipboard = (wfClipboard*)((CREATESTRUCT*)lParam)->lpCreateParams;
1358 clipboard->hwnd = hWnd;
1359
1360 if (!clipboard->legacyApi)
1361 clipboard->AddClipboardFormatListener(hWnd);
1362 else
1363 clipboard->hWndNextViewer = SetClipboardViewer(hWnd);
1364
1365 break;
1366
1367 case WM_CLOSE:
1368 DEBUG_CLIPRDR("info: WM_CLOSE");
1369
1370 if (!clipboard->legacyApi)
1371 clipboard->RemoveClipboardFormatListener(hWnd);
1372
1373 break;
1374
1375 case WM_DESTROY:
1376 if (clipboard->legacyApi)
1377 ChangeClipboardChain(hWnd, clipboard->hWndNextViewer);
1378
1379 break;
1380
1381 case WM_CLIPBOARDUPDATE:
1382 DEBUG_CLIPRDR("info: WM_CLIPBOARDUPDATE");
1383
1384 if (clipboard->sync)
1385 {
1386 if ((GetClipboardOwner() != clipboard->hwnd) &&
1387 (S_FALSE == OleIsCurrentClipboard(clipboard->data_obj)))
1388 {
1389 if (clipboard->hmem)
1390 {
1391 GlobalFree(clipboard->hmem);
1392 clipboard->hmem = nullptr;
1393 }
1394
1395 cliprdr_send_format_list(clipboard);
1396 }
1397 }
1398
1399 break;
1400
1401 case WM_RENDERALLFORMATS:
1402 DEBUG_CLIPRDR("info: WM_RENDERALLFORMATS");
1403
1404 /* discard all contexts in clipboard */
1405 if (!try_open_clipboard(clipboard->hwnd))
1406 {
1407 DEBUG_CLIPRDR("OpenClipboard failed with 0x%x", GetLastError());
1408 break;
1409 }
1410
1411 EmptyClipboard();
1412 CloseClipboard();
1413 break;
1414
1415 case WM_RENDERFORMAT:
1416 DEBUG_CLIPRDR("info: WM_RENDERFORMAT");
1417
1418 if (cliprdr_send_data_request(clipboard, (UINT32)wParam) != 0)
1419 {
1420 DEBUG_CLIPRDR("error: cliprdr_send_data_request failed.");
1421 break;
1422 }
1423
1424 if (!SetClipboardData((UINT)wParam, clipboard->hmem))
1425 {
1426 DEBUG_CLIPRDR("SetClipboardData failed with 0x%x", GetLastError());
1427
1428 if (clipboard->hmem)
1429 {
1430 GlobalFree(clipboard->hmem);
1431 clipboard->hmem = nullptr;
1432 }
1433 }
1434
1435 /* Note: GlobalFree() is not needed when success */
1436 break;
1437
1438 case WM_DRAWCLIPBOARD:
1439 if (clipboard->legacyApi)
1440 {
1441 if ((GetClipboardOwner() != clipboard->hwnd) &&
1442 (S_FALSE == OleIsCurrentClipboard(clipboard->data_obj)))
1443 {
1444 cliprdr_send_format_list(clipboard);
1445 }
1446
1447 SendMessage(clipboard->hWndNextViewer, Msg, wParam, lParam);
1448 }
1449
1450 break;
1451
1452 case WM_CHANGECBCHAIN:
1453 if (clipboard->legacyApi)
1454 {
1455 HWND hWndCurrViewer = (HWND)wParam;
1456 HWND hWndNextViewer = (HWND)lParam;
1457
1458 if (hWndCurrViewer == clipboard->hWndNextViewer)
1459 clipboard->hWndNextViewer = hWndNextViewer;
1460 else if (clipboard->hWndNextViewer)
1461 SendMessage(clipboard->hWndNextViewer, Msg, wParam, lParam);
1462 }
1463
1464 break;
1465
1466 case WM_CLIPRDR_MESSAGE:
1467 DEBUG_CLIPRDR("info: WM_CLIPRDR_MESSAGE");
1468
1469 switch (wParam)
1470 {
1471 case OLE_SETCLIPBOARD:
1472 DEBUG_CLIPRDR("info: OLE_SETCLIPBOARD");
1473
1474 if (wf_create_file_obj(clipboard, &clipboard->data_obj))
1475 {
1476 if (OleSetClipboard(clipboard->data_obj) != S_OK)
1477 {
1478 wf_destroy_file_obj(clipboard->data_obj);
1479 clipboard->data_obj = nullptr;
1480 }
1481 }
1482
1483 break;
1484
1485 default:
1486 break;
1487 }
1488
1489 break;
1490
1491 case WM_DESTROYCLIPBOARD:
1492 case WM_ASKCBFORMATNAME:
1493 case WM_HSCROLLCLIPBOARD:
1494 case WM_PAINTCLIPBOARD:
1495 case WM_SIZECLIPBOARD:
1496 case WM_VSCROLLCLIPBOARD:
1497 default:
1498 return DefWindowProc(hWnd, Msg, wParam, lParam);
1499 }
1500
1501 return 0;
1502}
1503
1504static int create_cliprdr_window(wfClipboard* clipboard)
1505{
1506 WNDCLASSEX wnd_cls = WINPR_C_ARRAY_INIT;
1507
1508 wnd_cls.cbSize = sizeof(WNDCLASSEX);
1509 wnd_cls.style = CS_OWNDC;
1510 wnd_cls.lpfnWndProc = cliprdr_proc;
1511 wnd_cls.cbClsExtra = 0;
1512 wnd_cls.cbWndExtra = 0;
1513 wnd_cls.hIcon = nullptr;
1514 wnd_cls.hCursor = nullptr;
1515 wnd_cls.hbrBackground = nullptr;
1516 wnd_cls.lpszMenuName = nullptr;
1517 wnd_cls.lpszClassName = _T("ClipboardHiddenMessageProcessor");
1518 wnd_cls.hInstance = GetModuleHandle(nullptr);
1519 wnd_cls.hIconSm = nullptr;
1520 RegisterClassEx(&wnd_cls);
1521 clipboard->hwnd =
1522 CreateWindowEx(WS_EX_LEFT, _T("ClipboardHiddenMessageProcessor"), _T("rdpclip"), 0, 0, 0, 0,
1523 0, HWND_MESSAGE, nullptr, GetModuleHandle(nullptr), clipboard);
1524
1525 if (!clipboard->hwnd)
1526 {
1527 DEBUG_CLIPRDR("error: CreateWindowEx failed with %x.", GetLastError());
1528 return -1;
1529 }
1530
1531 return 0;
1532}
1533
1534static DWORD WINAPI cliprdr_thread_func(LPVOID arg)
1535{
1536 int ret;
1537 MSG msg;
1538 BOOL mcode;
1539 wfClipboard* clipboard = (wfClipboard*)arg;
1540 OleInitialize(0);
1541
1542 if ((ret = create_cliprdr_window(clipboard)) != 0)
1543 {
1544 OleUninitialize();
1545 DEBUG_CLIPRDR("error: create clipboard window failed.");
1546 return 0;
1547 }
1548
1549 while ((mcode = GetMessage(&msg, 0, 0, 0)) != 0)
1550 {
1551 if (mcode == -1)
1552 {
1553 DEBUG_CLIPRDR("error: clipboard thread GetMessage failed.");
1554 break;
1555 }
1556 else
1557 {
1558 TranslateMessage(&msg);
1559 DispatchMessage(&msg);
1560 }
1561 }
1562
1563 OleUninitialize();
1564 return 0;
1565}
1566
1567static void clear_file_array(wfClipboard* clipboard)
1568{
1569 if (!clipboard)
1570 return;
1571
1572 /* clear file_names array */
1573 if (clipboard->file_names)
1574 {
1575 for (size_t i = 0; i < clipboard->nFiles; i++)
1576 {
1577 free(clipboard->file_names[i]);
1578 clipboard->file_names[i] = nullptr;
1579 }
1580
1581 free(clipboard->file_names);
1582 clipboard->file_names = nullptr;
1583 }
1584
1585 /* clear fileDescriptor array */
1586 if (clipboard->fileDescriptor)
1587 {
1588 for (size_t i = 0; i < clipboard->nFiles; i++)
1589 {
1590 free(clipboard->fileDescriptor[i]);
1591 clipboard->fileDescriptor[i] = nullptr;
1592 }
1593
1594 free(clipboard->fileDescriptor);
1595 clipboard->fileDescriptor = nullptr;
1596 }
1597
1598 clipboard->file_array_size = 0;
1599 clipboard->nFiles = 0;
1600}
1601
1602static BOOL wf_cliprdr_get_file_contents(WCHAR* file_name, BYTE* buffer, LONG positionLow,
1603 LONG positionHigh, DWORD nRequested, DWORD* puSize)
1604{
1605 BOOL res = FALSE;
1606 HANDLE hFile;
1607 DWORD nGet, rc;
1608
1609 if (!file_name || !buffer || !puSize)
1610 {
1611 WLog_ERR(TAG, "get file contents Invalid Arguments.");
1612 return FALSE;
1613 }
1614
1615 hFile = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
1616 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1617
1618 if (hFile == INVALID_HANDLE_VALUE)
1619 return FALSE;
1620
1621 rc = SetFilePointer(hFile, positionLow, &positionHigh, FILE_BEGIN);
1622
1623 if (rc == INVALID_SET_FILE_POINTER)
1624 goto error;
1625
1626 if (!ReadFile(hFile, buffer, nRequested, &nGet, nullptr))
1627 {
1628 DEBUG_CLIPRDR("ReadFile failed with 0x%08lX.", GetLastError());
1629 goto error;
1630 }
1631
1632 res = TRUE;
1633error:
1634
1635 if (!CloseHandle(hFile))
1636 res = FALSE;
1637
1638 if (res)
1639 *puSize = nGet;
1640
1641 return res;
1642}
1643
1644/* path_name has a '\' at the end. e.g. c:\newfolder\, file_name is c:\newfolder\new.txt */
1645static FILEDESCRIPTORW* wf_cliprdr_get_file_descriptor(WCHAR* file_name, size_t pathLen)
1646{
1647 HANDLE hFile;
1648 FILEDESCRIPTORW* fd;
1649 fd = (FILEDESCRIPTORW*)calloc(1, sizeof(FILEDESCRIPTORW));
1650
1651 if (!fd)
1652 return nullptr;
1653
1654 hFile = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
1655 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1656
1657 if (hFile == INVALID_HANDLE_VALUE)
1658 {
1659 free(fd);
1660 return nullptr;
1661 }
1662
1663 fd->dwFlags = FD_ATTRIBUTES | FD_FILESIZE | FD_WRITESTIME | FD_PROGRESSUI;
1664 fd->dwFileAttributes = GetFileAttributes(file_name);
1665
1666 if (!GetFileTime(hFile, nullptr, nullptr, &fd->ftLastWriteTime))
1667 {
1668 fd->dwFlags &= ~FD_WRITESTIME;
1669 }
1670
1671 fd->nFileSizeLow = GetFileSize(hFile, &fd->nFileSizeHigh);
1672 wcscpy_s(fd->cFileName, sizeof(fd->cFileName) / 2, file_name + pathLen);
1673 (void)CloseHandle(hFile);
1674 return fd;
1675}
1676
1677static BOOL wf_cliprdr_array_ensure_capacity(wfClipboard* clipboard)
1678{
1679 if (!clipboard)
1680 return FALSE;
1681
1682 if (clipboard->nFiles == clipboard->file_array_size)
1683 {
1684 size_t new_size;
1685 FILEDESCRIPTORW** new_fd;
1686 WCHAR** new_name;
1687 new_size = (clipboard->file_array_size + 1) * 2;
1688 new_fd = (FILEDESCRIPTORW**)realloc(clipboard->fileDescriptor,
1689 new_size * sizeof(FILEDESCRIPTORW*));
1690
1691 if (new_fd)
1692 clipboard->fileDescriptor = new_fd;
1693
1694 new_name = (WCHAR**)realloc(clipboard->file_names, new_size * sizeof(WCHAR*));
1695
1696 if (new_name)
1697 clipboard->file_names = new_name;
1698
1699 if (!new_fd || !new_name)
1700 return FALSE;
1701
1702 clipboard->file_array_size = new_size;
1703 }
1704
1705 return TRUE;
1706}
1707
1708static BOOL wf_cliprdr_add_to_file_arrays(wfClipboard* clipboard, WCHAR* full_file_name,
1709 size_t pathLen)
1710{
1711 if (!wf_cliprdr_array_ensure_capacity(clipboard))
1712 return FALSE;
1713
1714 /* add to name array */
1715 clipboard->file_names[clipboard->nFiles] = (LPWSTR)malloc(MAX_PATH * 2);
1716
1717 if (!clipboard->file_names[clipboard->nFiles])
1718 return FALSE;
1719
1720 wcscpy_s(clipboard->file_names[clipboard->nFiles], MAX_PATH, full_file_name);
1721 /* add to descriptor array */
1722 clipboard->fileDescriptor[clipboard->nFiles] =
1723 wf_cliprdr_get_file_descriptor(full_file_name, pathLen);
1724
1725 if (!clipboard->fileDescriptor[clipboard->nFiles])
1726 {
1727 free(clipboard->file_names[clipboard->nFiles]);
1728 return FALSE;
1729 }
1730
1731 clipboard->nFiles++;
1732 return TRUE;
1733}
1734
1735static BOOL wf_cliprdr_traverse_directory(wfClipboard* clipboard, WCHAR* Dir, size_t pathLen)
1736{
1737 HANDLE hFind;
1738 WCHAR DirSpec[MAX_PATH];
1739 WIN32_FIND_DATA FindFileData;
1740
1741 if (!clipboard || !Dir)
1742 return FALSE;
1743
1744 StringCchCopy(DirSpec, MAX_PATH, Dir);
1745 StringCchCat(DirSpec, MAX_PATH, TEXT("\\*"));
1746 hFind = FindFirstFile(DirSpec, &FindFileData);
1747
1748 if (hFind == INVALID_HANDLE_VALUE)
1749 {
1750 DEBUG_CLIPRDR("FindFirstFile failed with 0x%x.", GetLastError());
1751 return FALSE;
1752 }
1753
1754 while (FindNextFile(hFind, &FindFileData))
1755 {
1756 if ((((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) &&
1757 (wcscmp(FindFileData.cFileName, _T(".")) == 0)) ||
1758 (wcscmp(FindFileData.cFileName, _T("..")) == 0))
1759 {
1760 continue;
1761 }
1762
1763 if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
1764 {
1765 WCHAR DirAdd[MAX_PATH];
1766 StringCchCopy(DirAdd, MAX_PATH, Dir);
1767 StringCchCat(DirAdd, MAX_PATH, _T("\\"));
1768 StringCchCat(DirAdd, MAX_PATH, FindFileData.cFileName);
1769
1770 if (!wf_cliprdr_add_to_file_arrays(clipboard, DirAdd, pathLen))
1771 return FALSE;
1772
1773 if (!wf_cliprdr_traverse_directory(clipboard, DirAdd, pathLen))
1774 return FALSE;
1775 }
1776 else
1777 {
1778 WCHAR fileName[MAX_PATH];
1779 StringCchCopy(fileName, MAX_PATH, Dir);
1780 StringCchCat(fileName, MAX_PATH, _T("\\"));
1781 StringCchCat(fileName, MAX_PATH, FindFileData.cFileName);
1782
1783 if (!wf_cliprdr_add_to_file_arrays(clipboard, fileName, pathLen))
1784 return FALSE;
1785 }
1786 }
1787
1788 FindClose(hFind);
1789 return TRUE;
1790}
1791
1792static UINT wf_cliprdr_send_client_capabilities(wfClipboard* clipboard)
1793{
1794 CLIPRDR_CAPABILITIES capabilities;
1795 CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet;
1796
1797 if (!clipboard || !clipboard->context || !clipboard->context->ClientCapabilities)
1798 return ERROR_INTERNAL_ERROR;
1799
1800 capabilities.cCapabilitiesSets = 1;
1801 capabilities.capabilitySets = (CLIPRDR_CAPABILITY_SET*)&(generalCapabilitySet);
1802 generalCapabilitySet.capabilitySetType = CB_CAPSTYPE_GENERAL;
1803 generalCapabilitySet.capabilitySetLength = 12;
1804 generalCapabilitySet.version = CB_CAPS_VERSION_2;
1805 generalCapabilitySet.generalFlags =
1806 CB_USE_LONG_FORMAT_NAMES | CB_STREAM_FILECLIP_ENABLED | CB_FILECLIP_NO_FILE_PATHS;
1807 return clipboard->context->ClientCapabilities(clipboard->context, &capabilities);
1808}
1809
1815static UINT wf_cliprdr_monitor_ready(CliprdrClientContext* context,
1816 const CLIPRDR_MONITOR_READY* monitorReady)
1817{
1818 UINT rc;
1819 wfClipboard* clipboard = (wfClipboard*)context->custom;
1820
1821 if (!context || !monitorReady)
1822 return ERROR_INTERNAL_ERROR;
1823
1824 clipboard->sync = TRUE;
1825 rc = wf_cliprdr_send_client_capabilities(clipboard);
1826
1827 if (rc != CHANNEL_RC_OK)
1828 return rc;
1829
1830 return cliprdr_send_format_list(clipboard);
1831}
1832
1838static UINT wf_cliprdr_server_capabilities(CliprdrClientContext* context,
1839 const CLIPRDR_CAPABILITIES* capabilities)
1840{
1841 CLIPRDR_CAPABILITY_SET* capabilitySet;
1842 wfClipboard* clipboard = (wfClipboard*)context->custom;
1843
1844 if (!context || !capabilities)
1845 return ERROR_INTERNAL_ERROR;
1846
1847 for (UINT32 index = 0; index < capabilities->cCapabilitiesSets; index++)
1848 {
1849 capabilitySet = &(capabilities->capabilitySets[index]);
1850
1851 if ((capabilitySet->capabilitySetType == CB_CAPSTYPE_GENERAL) &&
1852 (capabilitySet->capabilitySetLength >= CB_CAPSTYPE_GENERAL_LEN))
1853 {
1854 CLIPRDR_GENERAL_CAPABILITY_SET* generalCapabilitySet =
1855 (CLIPRDR_GENERAL_CAPABILITY_SET*)capabilitySet;
1856 clipboard->capabilities = generalCapabilitySet->generalFlags;
1857 break;
1858 }
1859 }
1860
1861 return CHANNEL_RC_OK;
1862}
1863
1869static UINT wf_cliprdr_server_format_list(CliprdrClientContext* context,
1870 const CLIPRDR_FORMAT_LIST* formatList)
1871{
1872 UINT rc = ERROR_INTERNAL_ERROR;
1873 formatMapping* mapping;
1874 CLIPRDR_FORMAT* format;
1875 wfClipboard* clipboard = (wfClipboard*)context->custom;
1876
1877 if (!clear_format_map(clipboard))
1878 return ERROR_INTERNAL_ERROR;
1879
1880 for (UINT32 i = 0; i < formatList->numFormats; i++)
1881 {
1882 format = &(formatList->formats[i]);
1883 mapping = &(clipboard->format_mappings[i]);
1884 mapping->remote_format_id = format->formatId;
1885
1886 if (format->formatName)
1887 {
1888 mapping->name = ConvertUtf8ToWCharAlloc(format->formatName, nullptr);
1889
1890 if (mapping->name)
1891 mapping->local_format_id = RegisterClipboardFormatW((LPWSTR)mapping->name);
1892 }
1893 else
1894 {
1895 mapping->name = nullptr;
1896 mapping->local_format_id = mapping->remote_format_id;
1897 }
1898
1899 clipboard->map_size++;
1900 map_ensure_capacity(clipboard);
1901 }
1902
1903 if (file_transferring(clipboard))
1904 {
1905 if (PostMessage(clipboard->hwnd, WM_CLIPRDR_MESSAGE, OLE_SETCLIPBOARD, 0))
1906 rc = CHANNEL_RC_OK;
1907 }
1908 else
1909 {
1910 if (!try_open_clipboard(clipboard->hwnd))
1911 return CHANNEL_RC_OK; /* Ignore, other app holding clipboard */
1912
1913 if (EmptyClipboard())
1914 {
1915 for (UINT32 i = 0; i < (UINT32)clipboard->map_size; i++)
1916 SetClipboardData(clipboard->format_mappings[i].local_format_id, nullptr);
1917
1918 rc = CHANNEL_RC_OK;
1919 }
1920
1921 if (!CloseClipboard() && GetLastError())
1922 return ERROR_INTERNAL_ERROR;
1923 }
1924
1925 return rc;
1926}
1927
1933static UINT
1934wf_cliprdr_server_format_list_response(CliprdrClientContext* context,
1935 const CLIPRDR_FORMAT_LIST_RESPONSE* formatListResponse)
1936{
1937 (void)context;
1938 (void)formatListResponse;
1939
1940 if (formatListResponse->common.msgFlags != CB_RESPONSE_OK)
1941 WLog_WARN(TAG, "format list update failed");
1942
1943 return CHANNEL_RC_OK;
1944}
1945
1951static UINT
1952wf_cliprdr_server_lock_clipboard_data(CliprdrClientContext* context,
1953 const CLIPRDR_LOCK_CLIPBOARD_DATA* lockClipboardData)
1954{
1955 (void)context;
1956 (void)lockClipboardData;
1957 return CHANNEL_RC_OK;
1958}
1959
1965static UINT
1966wf_cliprdr_server_unlock_clipboard_data(CliprdrClientContext* context,
1967 const CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData)
1968{
1969 (void)context;
1970 (void)unlockClipboardData;
1971 return CHANNEL_RC_OK;
1972}
1973
1974static BOOL wf_cliprdr_process_filename(wfClipboard* clipboard, WCHAR* wFileName, size_t str_len)
1975{
1976 size_t pathLen;
1977 size_t offset = str_len;
1978
1979 if (!clipboard || !wFileName)
1980 return FALSE;
1981
1982 /* find the last '\' in full file name */
1983 while (offset > 0)
1984 {
1985 if (wFileName[offset] == L'\\')
1986 break;
1987 else
1988 offset--;
1989 }
1990
1991 pathLen = offset + 1;
1992
1993 if (!wf_cliprdr_add_to_file_arrays(clipboard, wFileName, pathLen))
1994 return FALSE;
1995
1996 if ((clipboard->fileDescriptor[clipboard->nFiles - 1]->dwFileAttributes &
1997 FILE_ATTRIBUTE_DIRECTORY) != 0)
1998 {
1999 /* this is a directory */
2000 if (!wf_cliprdr_traverse_directory(clipboard, wFileName, pathLen))
2001 return FALSE;
2002 }
2003
2004 return TRUE;
2005}
2006
2007static SSIZE_T wf_cliprdr_tryopen(wfClipboard* clipboard, UINT32 requestedFormatId, BYTE** pData)
2008{
2009 SSIZE_T rc = -1;
2010 WINPR_ASSERT(clipboard);
2011 WINPR_ASSERT(pData);
2012
2013 *pData = nullptr;
2014
2015 /* Ignore if other app is holding the clipboard */
2016 if (!try_open_clipboard(clipboard->hwnd))
2017 return 0;
2018
2019 HANDLE hClipdata = GetClipboardData(requestedFormatId);
2020
2021 if (!hClipdata)
2022 goto fail;
2023
2024 char* globlemem = (char*)GlobalLock(hClipdata);
2025 const SSIZE_T size = GlobalSize(hClipdata);
2026 if (size <= 0)
2027 goto unlock;
2028
2029 BYTE* buff = malloc(size);
2030 if (buff == nullptr)
2031 goto fail;
2032 CopyMemory(buff, globlemem, size);
2033 *pData = buff;
2034 rc = size;
2035
2036unlock:
2037 GlobalUnlock(hClipdata);
2038
2039fail:
2040 CloseClipboard();
2041
2042 return rc;
2043}
2044
2045static SSIZE_T wf_cliprdr_get_filedescriptor(wfClipboard* clipboard, BYTE** pData)
2046{
2047 WINPR_ASSERT(clipboard);
2048 WINPR_ASSERT(pData);
2049
2050 SSIZE_T rc = -1;
2051 LPDATAOBJECT dataObj = nullptr;
2052 FORMATETC format_etc = WINPR_C_ARRAY_INIT;
2053 STGMEDIUM stg_medium = WINPR_C_ARRAY_INIT;
2054
2055 *pData = nullptr;
2056
2057 HRESULT result = OleGetClipboard(&dataObj);
2058 if (FAILED(result))
2059 return -1;
2060
2061 /* get DROPFILES struct from OLE */
2062 format_etc.cfFormat = CF_HDROP;
2063 format_etc.tymed = TYMED_HGLOBAL;
2064 format_etc.dwAspect = 1;
2065 format_etc.lindex = -1;
2066 result = IDataObject_GetData(dataObj, &format_etc, &stg_medium);
2067
2068 if (FAILED(result))
2069 {
2070 DEBUG_CLIPRDR("dataObj->GetData failed.");
2071 goto exit;
2072 }
2073
2074 HGLOBAL hdl = stg_medium.u.hGlobal;
2075 DROPFILES* dropFiles = (DROPFILES*)GlobalLock(hdl);
2076
2077 if (!dropFiles)
2078 {
2079 ReleaseStgMedium(&stg_medium);
2080 clipboard->nFiles = 0;
2081 goto exit;
2082 }
2083
2084 clear_file_array(clipboard);
2085
2086 if (dropFiles->fWide)
2087 {
2088 /* dropFiles contains file names */
2089 size_t len = 0;
2090 for (WCHAR* wFileName = (WCHAR*)((char*)dropFiles + dropFiles->pFiles);
2091 (len = wcslen(wFileName)) > 0; wFileName += len + 1)
2092 {
2093 wf_cliprdr_process_filename(clipboard, wFileName, wcslen(wFileName));
2094 }
2095 }
2096 else
2097 {
2098 size_t len = 0;
2099 for (char* p = (char*)((char*)dropFiles + dropFiles->pFiles); (len = strlen(p)) > 0;
2100 p += len + 1, clipboard->nFiles++)
2101 {
2102 const int ilen = WINPR_ASSERTING_INT_CAST(int, len);
2103 const int cchWideChar = MultiByteToWideChar(CP_ACP, MB_COMPOSITE, p, ilen, nullptr, 0);
2104 WCHAR* wFileName = (LPWSTR)calloc(cchWideChar, sizeof(WCHAR));
2105 MultiByteToWideChar(CP_ACP, MB_COMPOSITE, p, ilen, wFileName, cchWideChar);
2106 wf_cliprdr_process_filename(clipboard, wFileName, cchWideChar);
2107 free(wFileName);
2108 }
2109 }
2110
2111 GlobalUnlock(hdl);
2112 ReleaseStgMedium(&stg_medium);
2113exit:
2114{
2115 const size_t size = 4ull + clipboard->nFiles * sizeof(FILEDESCRIPTORW);
2116 FILEGROUPDESCRIPTORW* groupDsc = (FILEGROUPDESCRIPTORW*)calloc(size, 1);
2117
2118 if (groupDsc)
2119 {
2120 groupDsc->cItems = WINPR_ASSERTING_INT_CAST(UINT, clipboard->nFiles);
2121
2122 for (size_t i = 0; i < clipboard->nFiles; i++)
2123 {
2124 if (clipboard->fileDescriptor[i])
2125 groupDsc->fgd[i] = *clipboard->fileDescriptor[i];
2126 }
2127
2128 *pData = (BYTE*)groupDsc;
2129 rc = size;
2130 }
2131}
2132
2133 IDataObject_Release(dataObj);
2134 return rc;
2135}
2136
2142static UINT
2143wf_cliprdr_server_format_data_request(CliprdrClientContext* context,
2144 const CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest)
2145{
2146 if (!context || !formatDataRequest)
2147 return ERROR_INTERNAL_ERROR;
2148
2149 wfClipboard* clipboard = (wfClipboard*)context->custom;
2150
2151 if (!clipboard)
2152 return ERROR_INTERNAL_ERROR;
2153
2154 const UINT32 requestedFormatId = formatDataRequest->requestedFormatId;
2155 BYTE* requestedFormatData = nullptr;
2156 SSIZE_T res = 0;
2157 if (requestedFormatId == RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW))
2158 {
2159 res = wf_cliprdr_get_filedescriptor(clipboard, &requestedFormatData);
2160 }
2161 else
2162 {
2163 res = wf_cliprdr_tryopen(clipboard, requestedFormatId, &requestedFormatData);
2164 }
2165
2166 UINT rc = ERROR_INTERNAL_ERROR;
2167 if (res >= 0)
2168 {
2169 const CLIPRDR_FORMAT_DATA_RESPONSE response = {
2170 .common = { .msgType = CB_FORMAT_DATA_RESPONSE,
2171 .msgFlags = CB_RESPONSE_OK,
2172 .dataLen = (uint32_t)res },
2173 .requestedFormatData = requestedFormatData
2174 };
2175
2176 rc = clipboard->context->ClientFormatDataResponse(clipboard->context, &response);
2177 }
2178 else
2179 {
2180 const CLIPRDR_FORMAT_DATA_RESPONSE response = { .common = { .msgType =
2181 CB_FORMAT_DATA_RESPONSE,
2182 .msgFlags = CB_RESPONSE_FAIL,
2183 .dataLen = 0 },
2184 .requestedFormatData = nullptr };
2185
2186 rc = clipboard->context->ClientFormatDataResponse(clipboard->context, &response);
2187 }
2188
2189 free(requestedFormatData);
2190 return rc;
2191}
2192
2198static UINT
2199wf_cliprdr_server_format_data_response(CliprdrClientContext* context,
2200 const CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse)
2201{
2202 BYTE* data;
2203 HANDLE hMem;
2204 wfClipboard* clipboard;
2205
2206 if (!context || !formatDataResponse)
2207 return ERROR_INTERNAL_ERROR;
2208
2209 clipboard = (wfClipboard*)context->custom;
2210
2211 if (!clipboard)
2212 return ERROR_INTERNAL_ERROR;
2213
2214 if (formatDataResponse->common.msgFlags != CB_RESPONSE_OK)
2215 {
2216 clipboard->hmem = nullptr;
2217
2218 if (!SetEvent(clipboard->response_data_event))
2219 return ERROR_INTERNAL_ERROR;
2220
2221 return CHANNEL_RC_OK;
2222 }
2223
2224 hMem = GlobalAlloc(GMEM_MOVEABLE, formatDataResponse->common.dataLen);
2225
2226 if (!hMem)
2227 return ERROR_INTERNAL_ERROR;
2228
2229 data = (BYTE*)GlobalLock(hMem);
2230
2231 if (!data)
2232 {
2233 GlobalFree(hMem);
2234 return ERROR_INTERNAL_ERROR;
2235 }
2236
2237 CopyMemory(data, formatDataResponse->requestedFormatData, formatDataResponse->common.dataLen);
2238
2239 if (!GlobalUnlock(hMem) && GetLastError())
2240 {
2241 GlobalFree(hMem);
2242 return ERROR_INTERNAL_ERROR;
2243 }
2244
2245 clipboard->hmem = hMem;
2246
2247 if (!SetEvent(clipboard->response_data_event))
2248 return ERROR_INTERNAL_ERROR;
2249
2250 return CHANNEL_RC_OK;
2251}
2252
2258static UINT
2259wf_cliprdr_server_file_contents_request(CliprdrClientContext* context,
2260 const CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest)
2261{
2262 DWORD uSize = 0;
2263 BYTE* pData = nullptr;
2264 HRESULT hRet = S_OK;
2265 FORMATETC vFormatEtc = WINPR_C_ARRAY_INIT;
2266 LPDATAOBJECT pDataObj = nullptr;
2267 STGMEDIUM vStgMedium = WINPR_C_ARRAY_INIT;
2268 BOOL bIsStreamFile = TRUE;
2269 static LPSTREAM pStreamStc = nullptr;
2270 static UINT32 uStreamIdStc = 0;
2271 wfClipboard* clipboard;
2272 UINT rc = ERROR_INTERNAL_ERROR;
2273 UINT sRc;
2274 UINT32 cbRequested;
2275
2276 if (!context || !fileContentsRequest)
2277 return ERROR_INTERNAL_ERROR;
2278
2279 clipboard = (wfClipboard*)context->custom;
2280
2281 if (!clipboard)
2282 return ERROR_INTERNAL_ERROR;
2283
2284 cbRequested = fileContentsRequest->cbRequested;
2285 if (fileContentsRequest->dwFlags == FILECONTENTS_SIZE)
2286 cbRequested = sizeof(UINT64);
2287
2288 pData = (BYTE*)calloc(1, cbRequested);
2289
2290 if (!pData)
2291 goto error;
2292
2293 hRet = OleGetClipboard(&pDataObj);
2294
2295 if (FAILED(hRet))
2296 {
2297 WLog_ERR(TAG, "filecontents: get ole clipboard failed.");
2298 goto error;
2299 }
2300
2301 vFormatEtc.cfFormat = RegisterClipboardFormat(CFSTR_FILECONTENTS);
2302 vFormatEtc.tymed = TYMED_ISTREAM;
2303 vFormatEtc.dwAspect = 1;
2304 vFormatEtc.lindex = fileContentsRequest->listIndex;
2305 vFormatEtc.ptd = nullptr;
2306
2307 if ((uStreamIdStc != fileContentsRequest->streamId) || !pStreamStc)
2308 {
2309 LPENUMFORMATETC pEnumFormatEtc;
2310 ULONG CeltFetched;
2311 FORMATETC vFormatEtc2;
2312
2313 if (pStreamStc)
2314 {
2315 IStream_Release(pStreamStc);
2316 pStreamStc = nullptr;
2317 }
2318
2319 bIsStreamFile = FALSE;
2320 hRet = IDataObject_EnumFormatEtc(pDataObj, DATADIR_GET, &pEnumFormatEtc);
2321
2322 if (hRet == S_OK)
2323 {
2324 do
2325 {
2326 hRet = IEnumFORMATETC_Next(pEnumFormatEtc, 1, &vFormatEtc2, &CeltFetched);
2327
2328 if (hRet == S_OK)
2329 {
2330 if (vFormatEtc2.cfFormat == RegisterClipboardFormat(CFSTR_FILECONTENTS))
2331 {
2332 hRet = IDataObject_GetData(pDataObj, &vFormatEtc, &vStgMedium);
2333
2334 if (hRet == S_OK)
2335 {
2336 pStreamStc = vStgMedium.u.pstm;
2337 uStreamIdStc = fileContentsRequest->streamId;
2338 bIsStreamFile = TRUE;
2339 }
2340
2341 break;
2342 }
2343 }
2344 } while (hRet == S_OK);
2345 }
2346 }
2347
2348 if (bIsStreamFile == TRUE)
2349 {
2350 if (fileContentsRequest->dwFlags == FILECONTENTS_SIZE)
2351 {
2352 STATSTG vStatStg = WINPR_C_ARRAY_INIT;
2353 hRet = IStream_Stat(pStreamStc, &vStatStg, STATFLAG_NONAME);
2354
2355 if (hRet == S_OK)
2356 {
2357 *((UINT32*)&pData[0]) = vStatStg.cbSize.QuadPart & 0xFFFFFFFF;
2358 *((UINT32*)&pData[4]) = (vStatStg.cbSize.QuadPart >> 32) & 0xFFFFFFFF;
2359 uSize = cbRequested;
2360 }
2361 }
2362 else if (fileContentsRequest->dwFlags == FILECONTENTS_RANGE)
2363 {
2364 LARGE_INTEGER dlibMove;
2365 ULARGE_INTEGER dlibNewPosition;
2366 dlibMove.QuadPart = (INT64)(((UINT64)fileContentsRequest->nPositionHigh << 32) |
2367 fileContentsRequest->nPositionLow);
2368 hRet = IStream_Seek(pStreamStc, dlibMove, STREAM_SEEK_SET, &dlibNewPosition);
2369
2370 if (SUCCEEDED(hRet))
2371 hRet = IStream_Read(pStreamStc, pData, cbRequested, (PULONG)&uSize);
2372 }
2373 }
2374 else
2375 {
2376 if (fileContentsRequest->dwFlags == FILECONTENTS_SIZE)
2377 {
2378 if (clipboard->nFiles <= fileContentsRequest->listIndex)
2379 goto error;
2380 *((UINT32*)&pData[0]) =
2381 clipboard->fileDescriptor[fileContentsRequest->listIndex]->nFileSizeLow;
2382 *((UINT32*)&pData[4]) =
2383 clipboard->fileDescriptor[fileContentsRequest->listIndex]->nFileSizeHigh;
2384 uSize = cbRequested;
2385 }
2386 else if (fileContentsRequest->dwFlags == FILECONTENTS_RANGE)
2387 {
2388 BOOL bRet;
2389 if (clipboard->nFiles <= fileContentsRequest->listIndex)
2390 goto error;
2391 bRet = wf_cliprdr_get_file_contents(
2392 clipboard->file_names[fileContentsRequest->listIndex], pData,
2393 fileContentsRequest->nPositionLow, fileContentsRequest->nPositionHigh, cbRequested,
2394 &uSize);
2395
2396 if (bRet == FALSE)
2397 {
2398 WLog_ERR(TAG, "get file contents failed.");
2399 uSize = 0;
2400 goto error;
2401 }
2402 }
2403 }
2404
2405 rc = CHANNEL_RC_OK;
2406error:
2407
2408 if (pDataObj)
2409 IDataObject_Release(pDataObj);
2410
2411 if (uSize == 0)
2412 {
2413 free(pData);
2414 pData = nullptr;
2415 }
2416
2417 sRc =
2418 cliprdr_send_response_filecontents(clipboard, fileContentsRequest->streamId, uSize, pData);
2419 free(pData);
2420
2421 if (sRc != CHANNEL_RC_OK)
2422 return sRc;
2423
2424 return rc;
2425}
2426
2432static UINT
2433wf_cliprdr_server_file_contents_response(CliprdrClientContext* context,
2434 const CLIPRDR_FILE_CONTENTS_RESPONSE* fileContentsResponse)
2435{
2436 wfClipboard* clipboard;
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 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}