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 == NULL)
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 = NULL;
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 = NULL;
625 }
626
627 pMedium->u.hGlobal = NULL;
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 = NULL;
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 = NULL;
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 NULL;
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] = { 0 };
825 STGMEDIUM stgmeds[2] = { 0 };
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 NULL;
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 NULL;
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;
1126 formatMapping* new_map;
1127 new_size = clipboard->map_capacity * 2;
1128 new_map =
1129 (formatMapping*)realloc(clipboard->format_mappings, sizeof(formatMapping) * new_size);
1130
1131 if (!new_map)
1132 return;
1133
1134 clipboard->format_mappings = new_map;
1135 clipboard->map_capacity = new_size;
1136 }
1137}
1138
1139static BOOL clear_format_map(wfClipboard* clipboard)
1140{
1141 formatMapping* map;
1142
1143 if (!clipboard)
1144 return FALSE;
1145
1146 if (clipboard->format_mappings)
1147 {
1148 for (size_t i = 0; i < clipboard->map_capacity; i++)
1149 {
1150 map = &clipboard->format_mappings[i];
1151 map->remote_format_id = 0;
1152 map->local_format_id = 0;
1153 free(map->name);
1154 map->name = NULL;
1155 }
1156 }
1157
1158 clipboard->map_size = 0;
1159 return TRUE;
1160}
1161
1162static UINT cliprdr_send_tempdir(wfClipboard* clipboard)
1163{
1164 CLIPRDR_TEMP_DIRECTORY tempDirectory;
1165
1166 if (!clipboard)
1167 return -1;
1168
1169 if (GetEnvironmentVariableA("TEMP", tempDirectory.szTempDir, sizeof(tempDirectory.szTempDir)) ==
1170 0)
1171 return -1;
1172
1173 return clipboard->context->TempDirectory(clipboard->context, &tempDirectory);
1174}
1175
1176static BOOL cliprdr_GetUpdatedClipboardFormats(wfClipboard* clipboard, PUINT lpuiFormats,
1177 UINT cFormats, PUINT pcFormatsOut)
1178{
1179 UINT index = 0;
1180 UINT format = 0;
1181 BOOL clipboardOpen = FALSE;
1182
1183 if (!clipboard->legacyApi)
1184 return clipboard->GetUpdatedClipboardFormats(lpuiFormats, cFormats, pcFormatsOut);
1185
1186 clipboardOpen = try_open_clipboard(clipboard->hwnd);
1187
1188 if (!clipboardOpen)
1189 {
1190 *pcFormatsOut = 0;
1191 return TRUE; /* Other app holding clipboard */
1192 }
1193
1194 while (index < cFormats)
1195 {
1196 format = EnumClipboardFormats(format);
1197
1198 if (!format)
1199 break;
1200
1201 lpuiFormats[index] = format;
1202 index++;
1203 }
1204
1205 *pcFormatsOut = index;
1206 CloseClipboard();
1207 return TRUE;
1208}
1209
1210static UINT cliprdr_send_format_list(wfClipboard* clipboard)
1211{
1212 UINT rc;
1213 int count = 0;
1214 UINT32 numFormats = 0;
1215 UINT32 formatId = 0;
1216 char formatName[1024];
1217 CLIPRDR_FORMAT* formats = NULL;
1218 CLIPRDR_FORMAT_LIST formatList = { 0 };
1219
1220 if (!clipboard)
1221 return ERROR_INTERNAL_ERROR;
1222
1223 /* Ignore if other app is holding clipboard */
1224 if (try_open_clipboard(clipboard->hwnd))
1225 {
1226 count = CountClipboardFormats();
1227 numFormats = (UINT32)count;
1228 formats = (CLIPRDR_FORMAT*)calloc(numFormats, sizeof(CLIPRDR_FORMAT));
1229
1230 if (!formats)
1231 {
1232 CloseClipboard();
1233 return CHANNEL_RC_NO_MEMORY;
1234 }
1235
1236 {
1237 UINT32 index = 0;
1238
1239 if (IsClipboardFormatAvailable(CF_HDROP))
1240 {
1241 formats[index++].formatId = RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW);
1242 formats[index++].formatId = RegisterClipboardFormat(CFSTR_FILECONTENTS);
1243 }
1244 else
1245 {
1246 while ((formatId = EnumClipboardFormats(formatId)) != 0)
1247 formats[index++].formatId = formatId;
1248 }
1249
1250 numFormats = index;
1251 }
1252
1253 if (!CloseClipboard())
1254 {
1255 free(formats);
1256 return ERROR_INTERNAL_ERROR;
1257 }
1258
1259 for (UINT index = 0; index < numFormats; index++)
1260 {
1261 if (GetClipboardFormatNameA(formats[index].formatId, formatName, sizeof(formatName)))
1262 {
1263 formats[index].formatName = _strdup(formatName);
1264 }
1265 }
1266 }
1267
1268 formatList.numFormats = numFormats;
1269 formatList.formats = formats;
1270 formatList.common.msgType = CB_FORMAT_LIST;
1271 rc = clipboard->context->ClientFormatList(clipboard->context, &formatList);
1272
1273 for (UINT index = 0; index < numFormats; index++)
1274 free(formats[index].formatName);
1275
1276 free(formats);
1277 return rc;
1278}
1279
1280static UINT cliprdr_send_data_request(wfClipboard* clipboard, UINT32 formatId)
1281{
1282 UINT rc;
1283 UINT32 remoteFormatId;
1284 CLIPRDR_FORMAT_DATA_REQUEST formatDataRequest;
1285
1286 if (!clipboard || !clipboard->context || !clipboard->context->ClientFormatDataRequest)
1287 return ERROR_INTERNAL_ERROR;
1288
1289 remoteFormatId = get_remote_format_id(clipboard, formatId);
1290
1291 formatDataRequest.requestedFormatId = remoteFormatId;
1292 clipboard->requestedFormatId = formatId;
1293 rc = clipboard->context->ClientFormatDataRequest(clipboard->context, &formatDataRequest);
1294
1295 if (WaitForSingleObject(clipboard->response_data_event, INFINITE) != WAIT_OBJECT_0)
1296 rc = ERROR_INTERNAL_ERROR;
1297 else if (!ResetEvent(clipboard->response_data_event))
1298 rc = ERROR_INTERNAL_ERROR;
1299
1300 return rc;
1301}
1302
1303UINT cliprdr_send_request_filecontents(wfClipboard* clipboard, const void* streamid, ULONG index,
1304 UINT32 flag, UINT64 position, ULONG nreq)
1305{
1306 UINT rc;
1307 CLIPRDR_FILE_CONTENTS_REQUEST fileContentsRequest;
1308
1309 if (!clipboard || !clipboard->context || !clipboard->context->ClientFileContentsRequest)
1310 return ERROR_INTERNAL_ERROR;
1311
1312 fileContentsRequest.streamId = (UINT32)(ULONG_PTR)streamid;
1313 fileContentsRequest.listIndex = index;
1314 fileContentsRequest.dwFlags = flag;
1315 fileContentsRequest.nPositionLow = position & 0xFFFFFFFF;
1316 fileContentsRequest.nPositionHigh = (position >> 32) & 0xFFFFFFFF;
1317 fileContentsRequest.cbRequested = nreq;
1318 fileContentsRequest.clipDataId = 0;
1319 fileContentsRequest.common.msgFlags = 0;
1320 rc = clipboard->context->ClientFileContentsRequest(clipboard->context, &fileContentsRequest);
1321
1322 if (WaitForSingleObject(clipboard->req_fevent, INFINITE) != WAIT_OBJECT_0)
1323 rc = ERROR_INTERNAL_ERROR;
1324 else if (!ResetEvent(clipboard->req_fevent))
1325 rc = ERROR_INTERNAL_ERROR;
1326
1327 return rc;
1328}
1329
1330static UINT cliprdr_send_response_filecontents(wfClipboard* clipboard, UINT32 streamId, UINT32 size,
1331 BYTE* data)
1332{
1333 CLIPRDR_FILE_CONTENTS_RESPONSE fileContentsResponse;
1334
1335 if (!clipboard || !clipboard->context || !clipboard->context->ClientFileContentsResponse)
1336 return ERROR_INTERNAL_ERROR;
1337
1338 fileContentsResponse.streamId = streamId;
1339 fileContentsResponse.cbRequested = size;
1340 fileContentsResponse.requestedData = data;
1341 fileContentsResponse.common.msgFlags = CB_RESPONSE_OK;
1342 return clipboard->context->ClientFileContentsResponse(clipboard->context,
1343 &fileContentsResponse);
1344}
1345
1346static LRESULT CALLBACK cliprdr_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
1347{
1348 static wfClipboard* clipboard = NULL;
1349
1350 switch (Msg)
1351 {
1352 case WM_CREATE:
1353 DEBUG_CLIPRDR("info: WM_CREATE");
1354 clipboard = (wfClipboard*)((CREATESTRUCT*)lParam)->lpCreateParams;
1355 clipboard->hwnd = hWnd;
1356
1357 if (!clipboard->legacyApi)
1358 clipboard->AddClipboardFormatListener(hWnd);
1359 else
1360 clipboard->hWndNextViewer = SetClipboardViewer(hWnd);
1361
1362 break;
1363
1364 case WM_CLOSE:
1365 DEBUG_CLIPRDR("info: WM_CLOSE");
1366
1367 if (!clipboard->legacyApi)
1368 clipboard->RemoveClipboardFormatListener(hWnd);
1369
1370 break;
1371
1372 case WM_DESTROY:
1373 if (clipboard->legacyApi)
1374 ChangeClipboardChain(hWnd, clipboard->hWndNextViewer);
1375
1376 break;
1377
1378 case WM_CLIPBOARDUPDATE:
1379 DEBUG_CLIPRDR("info: WM_CLIPBOARDUPDATE");
1380
1381 if (clipboard->sync)
1382 {
1383 if ((GetClipboardOwner() != clipboard->hwnd) &&
1384 (S_FALSE == OleIsCurrentClipboard(clipboard->data_obj)))
1385 {
1386 if (clipboard->hmem)
1387 {
1388 GlobalFree(clipboard->hmem);
1389 clipboard->hmem = NULL;
1390 }
1391
1392 cliprdr_send_format_list(clipboard);
1393 }
1394 }
1395
1396 break;
1397
1398 case WM_RENDERALLFORMATS:
1399 DEBUG_CLIPRDR("info: WM_RENDERALLFORMATS");
1400
1401 /* discard all contexts in clipboard */
1402 if (!try_open_clipboard(clipboard->hwnd))
1403 {
1404 DEBUG_CLIPRDR("OpenClipboard failed with 0x%x", GetLastError());
1405 break;
1406 }
1407
1408 EmptyClipboard();
1409 CloseClipboard();
1410 break;
1411
1412 case WM_RENDERFORMAT:
1413 DEBUG_CLIPRDR("info: WM_RENDERFORMAT");
1414
1415 if (cliprdr_send_data_request(clipboard, (UINT32)wParam) != 0)
1416 {
1417 DEBUG_CLIPRDR("error: cliprdr_send_data_request failed.");
1418 break;
1419 }
1420
1421 if (!SetClipboardData((UINT)wParam, clipboard->hmem))
1422 {
1423 DEBUG_CLIPRDR("SetClipboardData failed with 0x%x", GetLastError());
1424
1425 if (clipboard->hmem)
1426 {
1427 GlobalFree(clipboard->hmem);
1428 clipboard->hmem = NULL;
1429 }
1430 }
1431
1432 /* Note: GlobalFree() is not needed when success */
1433 break;
1434
1435 case WM_DRAWCLIPBOARD:
1436 if (clipboard->legacyApi)
1437 {
1438 if ((GetClipboardOwner() != clipboard->hwnd) &&
1439 (S_FALSE == OleIsCurrentClipboard(clipboard->data_obj)))
1440 {
1441 cliprdr_send_format_list(clipboard);
1442 }
1443
1444 SendMessage(clipboard->hWndNextViewer, Msg, wParam, lParam);
1445 }
1446
1447 break;
1448
1449 case WM_CHANGECBCHAIN:
1450 if (clipboard->legacyApi)
1451 {
1452 HWND hWndCurrViewer = (HWND)wParam;
1453 HWND hWndNextViewer = (HWND)lParam;
1454
1455 if (hWndCurrViewer == clipboard->hWndNextViewer)
1456 clipboard->hWndNextViewer = hWndNextViewer;
1457 else if (clipboard->hWndNextViewer)
1458 SendMessage(clipboard->hWndNextViewer, Msg, wParam, lParam);
1459 }
1460
1461 break;
1462
1463 case WM_CLIPRDR_MESSAGE:
1464 DEBUG_CLIPRDR("info: WM_CLIPRDR_MESSAGE");
1465
1466 switch (wParam)
1467 {
1468 case OLE_SETCLIPBOARD:
1469 DEBUG_CLIPRDR("info: OLE_SETCLIPBOARD");
1470
1471 if (wf_create_file_obj(clipboard, &clipboard->data_obj))
1472 {
1473 if (OleSetClipboard(clipboard->data_obj) != S_OK)
1474 {
1475 wf_destroy_file_obj(clipboard->data_obj);
1476 clipboard->data_obj = NULL;
1477 }
1478 }
1479
1480 break;
1481
1482 default:
1483 break;
1484 }
1485
1486 break;
1487
1488 case WM_DESTROYCLIPBOARD:
1489 case WM_ASKCBFORMATNAME:
1490 case WM_HSCROLLCLIPBOARD:
1491 case WM_PAINTCLIPBOARD:
1492 case WM_SIZECLIPBOARD:
1493 case WM_VSCROLLCLIPBOARD:
1494 default:
1495 return DefWindowProc(hWnd, Msg, wParam, lParam);
1496 }
1497
1498 return 0;
1499}
1500
1501static int create_cliprdr_window(wfClipboard* clipboard)
1502{
1503 WNDCLASSEX wnd_cls = { 0 };
1504
1505 wnd_cls.cbSize = sizeof(WNDCLASSEX);
1506 wnd_cls.style = CS_OWNDC;
1507 wnd_cls.lpfnWndProc = cliprdr_proc;
1508 wnd_cls.cbClsExtra = 0;
1509 wnd_cls.cbWndExtra = 0;
1510 wnd_cls.hIcon = NULL;
1511 wnd_cls.hCursor = NULL;
1512 wnd_cls.hbrBackground = NULL;
1513 wnd_cls.lpszMenuName = NULL;
1514 wnd_cls.lpszClassName = _T("ClipboardHiddenMessageProcessor");
1515 wnd_cls.hInstance = GetModuleHandle(NULL);
1516 wnd_cls.hIconSm = NULL;
1517 RegisterClassEx(&wnd_cls);
1518 clipboard->hwnd =
1519 CreateWindowEx(WS_EX_LEFT, _T("ClipboardHiddenMessageProcessor"), _T("rdpclip"), 0, 0, 0, 0,
1520 0, HWND_MESSAGE, NULL, GetModuleHandle(NULL), clipboard);
1521
1522 if (!clipboard->hwnd)
1523 {
1524 DEBUG_CLIPRDR("error: CreateWindowEx failed with %x.", GetLastError());
1525 return -1;
1526 }
1527
1528 return 0;
1529}
1530
1531static DWORD WINAPI cliprdr_thread_func(LPVOID arg)
1532{
1533 int ret;
1534 MSG msg;
1535 BOOL mcode;
1536 wfClipboard* clipboard = (wfClipboard*)arg;
1537 OleInitialize(0);
1538
1539 if ((ret = create_cliprdr_window(clipboard)) != 0)
1540 {
1541 OleUninitialize();
1542 DEBUG_CLIPRDR("error: create clipboard window failed.");
1543 return 0;
1544 }
1545
1546 while ((mcode = GetMessage(&msg, 0, 0, 0)) != 0)
1547 {
1548 if (mcode == -1)
1549 {
1550 DEBUG_CLIPRDR("error: clipboard thread GetMessage failed.");
1551 break;
1552 }
1553 else
1554 {
1555 TranslateMessage(&msg);
1556 DispatchMessage(&msg);
1557 }
1558 }
1559
1560 OleUninitialize();
1561 return 0;
1562}
1563
1564static void clear_file_array(wfClipboard* clipboard)
1565{
1566 if (!clipboard)
1567 return;
1568
1569 /* clear file_names array */
1570 if (clipboard->file_names)
1571 {
1572 for (size_t i = 0; i < clipboard->nFiles; i++)
1573 {
1574 free(clipboard->file_names[i]);
1575 clipboard->file_names[i] = NULL;
1576 }
1577
1578 free(clipboard->file_names);
1579 clipboard->file_names = NULL;
1580 }
1581
1582 /* clear fileDescriptor array */
1583 if (clipboard->fileDescriptor)
1584 {
1585 for (size_t i = 0; i < clipboard->nFiles; i++)
1586 {
1587 free(clipboard->fileDescriptor[i]);
1588 clipboard->fileDescriptor[i] = NULL;
1589 }
1590
1591 free(clipboard->fileDescriptor);
1592 clipboard->fileDescriptor = NULL;
1593 }
1594
1595 clipboard->file_array_size = 0;
1596 clipboard->nFiles = 0;
1597}
1598
1599static BOOL wf_cliprdr_get_file_contents(WCHAR* file_name, BYTE* buffer, LONG positionLow,
1600 LONG positionHigh, DWORD nRequested, DWORD* puSize)
1601{
1602 BOOL res = FALSE;
1603 HANDLE hFile;
1604 DWORD nGet, rc;
1605
1606 if (!file_name || !buffer || !puSize)
1607 {
1608 WLog_ERR(TAG, "get file contents Invalid Arguments.");
1609 return FALSE;
1610 }
1611
1612 hFile = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
1613 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL);
1614
1615 if (hFile == INVALID_HANDLE_VALUE)
1616 return FALSE;
1617
1618 rc = SetFilePointer(hFile, positionLow, &positionHigh, FILE_BEGIN);
1619
1620 if (rc == INVALID_SET_FILE_POINTER)
1621 goto error;
1622
1623 if (!ReadFile(hFile, buffer, nRequested, &nGet, NULL))
1624 {
1625 DEBUG_CLIPRDR("ReadFile failed with 0x%08lX.", GetLastError());
1626 goto error;
1627 }
1628
1629 res = TRUE;
1630error:
1631
1632 if (!CloseHandle(hFile))
1633 res = FALSE;
1634
1635 if (res)
1636 *puSize = nGet;
1637
1638 return res;
1639}
1640
1641/* path_name has a '\' at the end. e.g. c:\newfolder\, file_name is c:\newfolder\new.txt */
1642static FILEDESCRIPTORW* wf_cliprdr_get_file_descriptor(WCHAR* file_name, size_t pathLen)
1643{
1644 HANDLE hFile;
1645 FILEDESCRIPTORW* fd;
1646 fd = (FILEDESCRIPTORW*)calloc(1, sizeof(FILEDESCRIPTORW));
1647
1648 if (!fd)
1649 return NULL;
1650
1651 hFile = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
1652 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL);
1653
1654 if (hFile == INVALID_HANDLE_VALUE)
1655 {
1656 free(fd);
1657 return NULL;
1658 }
1659
1660 fd->dwFlags = FD_ATTRIBUTES | FD_FILESIZE | FD_WRITESTIME | FD_PROGRESSUI;
1661 fd->dwFileAttributes = GetFileAttributes(file_name);
1662
1663 if (!GetFileTime(hFile, NULL, NULL, &fd->ftLastWriteTime))
1664 {
1665 fd->dwFlags &= ~FD_WRITESTIME;
1666 }
1667
1668 fd->nFileSizeLow = GetFileSize(hFile, &fd->nFileSizeHigh);
1669 wcscpy_s(fd->cFileName, sizeof(fd->cFileName) / 2, file_name + pathLen);
1670 (void)CloseHandle(hFile);
1671 return fd;
1672}
1673
1674static BOOL wf_cliprdr_array_ensure_capacity(wfClipboard* clipboard)
1675{
1676 if (!clipboard)
1677 return FALSE;
1678
1679 if (clipboard->nFiles == clipboard->file_array_size)
1680 {
1681 size_t new_size;
1682 FILEDESCRIPTORW** new_fd;
1683 WCHAR** new_name;
1684 new_size = (clipboard->file_array_size + 1) * 2;
1685 new_fd = (FILEDESCRIPTORW**)realloc(clipboard->fileDescriptor,
1686 new_size * sizeof(FILEDESCRIPTORW*));
1687
1688 if (new_fd)
1689 clipboard->fileDescriptor = new_fd;
1690
1691 new_name = (WCHAR**)realloc(clipboard->file_names, new_size * sizeof(WCHAR*));
1692
1693 if (new_name)
1694 clipboard->file_names = new_name;
1695
1696 if (!new_fd || !new_name)
1697 return FALSE;
1698
1699 clipboard->file_array_size = new_size;
1700 }
1701
1702 return TRUE;
1703}
1704
1705static BOOL wf_cliprdr_add_to_file_arrays(wfClipboard* clipboard, WCHAR* full_file_name,
1706 size_t pathLen)
1707{
1708 if (!wf_cliprdr_array_ensure_capacity(clipboard))
1709 return FALSE;
1710
1711 /* add to name array */
1712 clipboard->file_names[clipboard->nFiles] = (LPWSTR)malloc(MAX_PATH * 2);
1713
1714 if (!clipboard->file_names[clipboard->nFiles])
1715 return FALSE;
1716
1717 wcscpy_s(clipboard->file_names[clipboard->nFiles], MAX_PATH, full_file_name);
1718 /* add to descriptor array */
1719 clipboard->fileDescriptor[clipboard->nFiles] =
1720 wf_cliprdr_get_file_descriptor(full_file_name, pathLen);
1721
1722 if (!clipboard->fileDescriptor[clipboard->nFiles])
1723 {
1724 free(clipboard->file_names[clipboard->nFiles]);
1725 return FALSE;
1726 }
1727
1728 clipboard->nFiles++;
1729 return TRUE;
1730}
1731
1732static BOOL wf_cliprdr_traverse_directory(wfClipboard* clipboard, WCHAR* Dir, size_t pathLen)
1733{
1734 HANDLE hFind;
1735 WCHAR DirSpec[MAX_PATH];
1736 WIN32_FIND_DATA FindFileData;
1737
1738 if (!clipboard || !Dir)
1739 return FALSE;
1740
1741 StringCchCopy(DirSpec, MAX_PATH, Dir);
1742 StringCchCat(DirSpec, MAX_PATH, TEXT("\\*"));
1743 hFind = FindFirstFile(DirSpec, &FindFileData);
1744
1745 if (hFind == INVALID_HANDLE_VALUE)
1746 {
1747 DEBUG_CLIPRDR("FindFirstFile failed with 0x%x.", GetLastError());
1748 return FALSE;
1749 }
1750
1751 while (FindNextFile(hFind, &FindFileData))
1752 {
1753 if ((((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) &&
1754 (wcscmp(FindFileData.cFileName, _T(".")) == 0)) ||
1755 (wcscmp(FindFileData.cFileName, _T("..")) == 0))
1756 {
1757 continue;
1758 }
1759
1760 if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
1761 {
1762 WCHAR DirAdd[MAX_PATH];
1763 StringCchCopy(DirAdd, MAX_PATH, Dir);
1764 StringCchCat(DirAdd, MAX_PATH, _T("\\"));
1765 StringCchCat(DirAdd, MAX_PATH, FindFileData.cFileName);
1766
1767 if (!wf_cliprdr_add_to_file_arrays(clipboard, DirAdd, pathLen))
1768 return FALSE;
1769
1770 if (!wf_cliprdr_traverse_directory(clipboard, DirAdd, pathLen))
1771 return FALSE;
1772 }
1773 else
1774 {
1775 WCHAR fileName[MAX_PATH];
1776 StringCchCopy(fileName, MAX_PATH, Dir);
1777 StringCchCat(fileName, MAX_PATH, _T("\\"));
1778 StringCchCat(fileName, MAX_PATH, FindFileData.cFileName);
1779
1780 if (!wf_cliprdr_add_to_file_arrays(clipboard, fileName, pathLen))
1781 return FALSE;
1782 }
1783 }
1784
1785 FindClose(hFind);
1786 return TRUE;
1787}
1788
1789static UINT wf_cliprdr_send_client_capabilities(wfClipboard* clipboard)
1790{
1791 CLIPRDR_CAPABILITIES capabilities;
1792 CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet;
1793
1794 if (!clipboard || !clipboard->context || !clipboard->context->ClientCapabilities)
1795 return ERROR_INTERNAL_ERROR;
1796
1797 capabilities.cCapabilitiesSets = 1;
1798 capabilities.capabilitySets = (CLIPRDR_CAPABILITY_SET*)&(generalCapabilitySet);
1799 generalCapabilitySet.capabilitySetType = CB_CAPSTYPE_GENERAL;
1800 generalCapabilitySet.capabilitySetLength = 12;
1801 generalCapabilitySet.version = CB_CAPS_VERSION_2;
1802 generalCapabilitySet.generalFlags =
1803 CB_USE_LONG_FORMAT_NAMES | CB_STREAM_FILECLIP_ENABLED | CB_FILECLIP_NO_FILE_PATHS;
1804 return clipboard->context->ClientCapabilities(clipboard->context, &capabilities);
1805}
1806
1812static UINT wf_cliprdr_monitor_ready(CliprdrClientContext* context,
1813 const CLIPRDR_MONITOR_READY* monitorReady)
1814{
1815 UINT rc;
1816 wfClipboard* clipboard = (wfClipboard*)context->custom;
1817
1818 if (!context || !monitorReady)
1819 return ERROR_INTERNAL_ERROR;
1820
1821 clipboard->sync = TRUE;
1822 rc = wf_cliprdr_send_client_capabilities(clipboard);
1823
1824 if (rc != CHANNEL_RC_OK)
1825 return rc;
1826
1827 return cliprdr_send_format_list(clipboard);
1828}
1829
1835static UINT wf_cliprdr_server_capabilities(CliprdrClientContext* context,
1836 const CLIPRDR_CAPABILITIES* capabilities)
1837{
1838 CLIPRDR_CAPABILITY_SET* capabilitySet;
1839 wfClipboard* clipboard = (wfClipboard*)context->custom;
1840
1841 if (!context || !capabilities)
1842 return ERROR_INTERNAL_ERROR;
1843
1844 for (UINT32 index = 0; index < capabilities->cCapabilitiesSets; index++)
1845 {
1846 capabilitySet = &(capabilities->capabilitySets[index]);
1847
1848 if ((capabilitySet->capabilitySetType == CB_CAPSTYPE_GENERAL) &&
1849 (capabilitySet->capabilitySetLength >= CB_CAPSTYPE_GENERAL_LEN))
1850 {
1851 CLIPRDR_GENERAL_CAPABILITY_SET* generalCapabilitySet =
1852 (CLIPRDR_GENERAL_CAPABILITY_SET*)capabilitySet;
1853 clipboard->capabilities = generalCapabilitySet->generalFlags;
1854 break;
1855 }
1856 }
1857
1858 return CHANNEL_RC_OK;
1859}
1860
1866static UINT wf_cliprdr_server_format_list(CliprdrClientContext* context,
1867 const CLIPRDR_FORMAT_LIST* formatList)
1868{
1869 UINT rc = ERROR_INTERNAL_ERROR;
1870 formatMapping* mapping;
1871 CLIPRDR_FORMAT* format;
1872 wfClipboard* clipboard = (wfClipboard*)context->custom;
1873
1874 if (!clear_format_map(clipboard))
1875 return ERROR_INTERNAL_ERROR;
1876
1877 for (UINT32 i = 0; i < formatList->numFormats; i++)
1878 {
1879 format = &(formatList->formats[i]);
1880 mapping = &(clipboard->format_mappings[i]);
1881 mapping->remote_format_id = format->formatId;
1882
1883 if (format->formatName)
1884 {
1885 mapping->name = ConvertUtf8ToWCharAlloc(format->formatName, NULL);
1886
1887 if (mapping->name)
1888 mapping->local_format_id = RegisterClipboardFormatW((LPWSTR)mapping->name);
1889 }
1890 else
1891 {
1892 mapping->name = NULL;
1893 mapping->local_format_id = mapping->remote_format_id;
1894 }
1895
1896 clipboard->map_size++;
1897 map_ensure_capacity(clipboard);
1898 }
1899
1900 if (file_transferring(clipboard))
1901 {
1902 if (PostMessage(clipboard->hwnd, WM_CLIPRDR_MESSAGE, OLE_SETCLIPBOARD, 0))
1903 rc = CHANNEL_RC_OK;
1904 }
1905 else
1906 {
1907 if (!try_open_clipboard(clipboard->hwnd))
1908 return CHANNEL_RC_OK; /* Ignore, other app holding clipboard */
1909
1910 if (EmptyClipboard())
1911 {
1912 for (UINT32 i = 0; i < (UINT32)clipboard->map_size; i++)
1913 SetClipboardData(clipboard->format_mappings[i].local_format_id, NULL);
1914
1915 rc = CHANNEL_RC_OK;
1916 }
1917
1918 if (!CloseClipboard() && GetLastError())
1919 return ERROR_INTERNAL_ERROR;
1920 }
1921
1922 return rc;
1923}
1924
1930static UINT
1931wf_cliprdr_server_format_list_response(CliprdrClientContext* context,
1932 const CLIPRDR_FORMAT_LIST_RESPONSE* formatListResponse)
1933{
1934 (void)context;
1935 (void)formatListResponse;
1936
1937 if (formatListResponse->common.msgFlags != CB_RESPONSE_OK)
1938 WLog_WARN(TAG, "format list update failed");
1939
1940 return CHANNEL_RC_OK;
1941}
1942
1948static UINT
1949wf_cliprdr_server_lock_clipboard_data(CliprdrClientContext* context,
1950 const CLIPRDR_LOCK_CLIPBOARD_DATA* lockClipboardData)
1951{
1952 (void)context;
1953 (void)lockClipboardData;
1954 return CHANNEL_RC_OK;
1955}
1956
1962static UINT
1963wf_cliprdr_server_unlock_clipboard_data(CliprdrClientContext* context,
1964 const CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData)
1965{
1966 (void)context;
1967 (void)unlockClipboardData;
1968 return CHANNEL_RC_OK;
1969}
1970
1971static BOOL wf_cliprdr_process_filename(wfClipboard* clipboard, WCHAR* wFileName, size_t str_len)
1972{
1973 size_t pathLen;
1974 size_t offset = str_len;
1975
1976 if (!clipboard || !wFileName)
1977 return FALSE;
1978
1979 /* find the last '\' in full file name */
1980 while (offset > 0)
1981 {
1982 if (wFileName[offset] == L'\\')
1983 break;
1984 else
1985 offset--;
1986 }
1987
1988 pathLen = offset + 1;
1989
1990 if (!wf_cliprdr_add_to_file_arrays(clipboard, wFileName, pathLen))
1991 return FALSE;
1992
1993 if ((clipboard->fileDescriptor[clipboard->nFiles - 1]->dwFileAttributes &
1994 FILE_ATTRIBUTE_DIRECTORY) != 0)
1995 {
1996 /* this is a directory */
1997 if (!wf_cliprdr_traverse_directory(clipboard, wFileName, pathLen))
1998 return FALSE;
1999 }
2000
2001 return TRUE;
2002}
2003
2004static SSIZE_T wf_cliprdr_tryopen(wfClipboard* clipboard, UINT32 requestedFormatId, BYTE** pData)
2005{
2006 SSIZE_T rc = -1;
2007 WINPR_ASSERT(clipboard);
2008 WINPR_ASSERT(pData);
2009
2010 *pData = NULL;
2011
2012 /* Ignore if other app is holding the clipboard */
2013 if (!try_open_clipboard(clipboard->hwnd))
2014 return 0;
2015
2016 HANDLE hClipdata = GetClipboardData(requestedFormatId);
2017
2018 if (!hClipdata)
2019 goto fail;
2020
2021 char* globlemem = (char*)GlobalLock(hClipdata);
2022 const SSIZE_T size = GlobalSize(hClipdata);
2023 if (size <= 0)
2024 goto unlock;
2025
2026 BYTE* buff = malloc(size);
2027 if (buff == NULL)
2028 goto fail;
2029 CopyMemory(buff, globlemem, size);
2030 *pData = buff;
2031 rc = size;
2032
2033unlock:
2034 GlobalUnlock(hClipdata);
2035
2036fail:
2037 CloseClipboard();
2038
2039 return rc;
2040}
2041
2042static SSIZE_T wf_cliprdr_get_filedescriptor(wfClipboard* clipboard, BYTE** pData)
2043{
2044 WINPR_ASSERT(clipboard);
2045 WINPR_ASSERT(pData);
2046
2047 SSIZE_T rc = -1;
2048 LPDATAOBJECT dataObj = NULL;
2049 FORMATETC format_etc = { 0 };
2050 STGMEDIUM stg_medium = { 0 };
2051
2052 *pData = NULL;
2053
2054 HRESULT result = OleGetClipboard(&dataObj);
2055 if (FAILED(result))
2056 return -1;
2057
2058 /* get DROPFILES struct from OLE */
2059 format_etc.cfFormat = CF_HDROP;
2060 format_etc.tymed = TYMED_HGLOBAL;
2061 format_etc.dwAspect = 1;
2062 format_etc.lindex = -1;
2063 result = IDataObject_GetData(dataObj, &format_etc, &stg_medium);
2064
2065 if (FAILED(result))
2066 {
2067 DEBUG_CLIPRDR("dataObj->GetData failed.");
2068 goto exit;
2069 }
2070
2071 HGLOBAL hdl = stg_medium.u.hGlobal;
2072 DROPFILES* dropFiles = (DROPFILES*)GlobalLock(hdl);
2073
2074 if (!dropFiles)
2075 {
2076 ReleaseStgMedium(&stg_medium);
2077 clipboard->nFiles = 0;
2078 goto exit;
2079 }
2080
2081 clear_file_array(clipboard);
2082
2083 if (dropFiles->fWide)
2084 {
2085 /* dropFiles contains file names */
2086 size_t len = 0;
2087 for (WCHAR* wFileName = (WCHAR*)((char*)dropFiles + dropFiles->pFiles);
2088 (len = wcslen(wFileName)) > 0; wFileName += len + 1)
2089 {
2090 wf_cliprdr_process_filename(clipboard, wFileName, wcslen(wFileName));
2091 }
2092 }
2093 else
2094 {
2095 size_t len = 0;
2096 for (char* p = (char*)((char*)dropFiles + dropFiles->pFiles); (len = strlen(p)) > 0;
2097 p += len + 1, clipboard->nFiles++)
2098 {
2099 const int ilen = WINPR_ASSERTING_INT_CAST(int, len);
2100 const int cchWideChar = MultiByteToWideChar(CP_ACP, MB_COMPOSITE, p, ilen, NULL, 0);
2101 WCHAR* wFileName = (LPWSTR)calloc(cchWideChar, sizeof(WCHAR));
2102 MultiByteToWideChar(CP_ACP, MB_COMPOSITE, p, ilen, wFileName, cchWideChar);
2103 wf_cliprdr_process_filename(clipboard, wFileName, cchWideChar);
2104 free(wFileName);
2105 }
2106 }
2107
2108 GlobalUnlock(hdl);
2109 ReleaseStgMedium(&stg_medium);
2110exit:
2111{
2112 const size_t size = 4ull + clipboard->nFiles * sizeof(FILEDESCRIPTORW);
2113 FILEGROUPDESCRIPTORW* groupDsc = (FILEGROUPDESCRIPTORW*)calloc(size, 1);
2114
2115 if (groupDsc)
2116 {
2117 groupDsc->cItems = WINPR_ASSERTING_INT_CAST(UINT, clipboard->nFiles);
2118
2119 for (size_t i = 0; i < clipboard->nFiles; i++)
2120 {
2121 if (clipboard->fileDescriptor[i])
2122 groupDsc->fgd[i] = *clipboard->fileDescriptor[i];
2123 }
2124
2125 *pData = (BYTE*)groupDsc;
2126 rc = size;
2127 }
2128}
2129
2130 IDataObject_Release(dataObj);
2131 return rc;
2132}
2133
2139static UINT
2140wf_cliprdr_server_format_data_request(CliprdrClientContext* context,
2141 const CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest)
2142{
2143 if (!context || !formatDataRequest)
2144 return ERROR_INTERNAL_ERROR;
2145
2146 wfClipboard* clipboard = (wfClipboard*)context->custom;
2147
2148 if (!clipboard)
2149 return ERROR_INTERNAL_ERROR;
2150
2151 const UINT32 requestedFormatId = formatDataRequest->requestedFormatId;
2152 BYTE* requestedFormatData = NULL;
2153 SSIZE_T res = 0;
2154 if (requestedFormatId == RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW))
2155 {
2156 res = wf_cliprdr_get_filedescriptor(clipboard, &requestedFormatData);
2157 }
2158 else
2159 {
2160 res = wf_cliprdr_tryopen(clipboard, requestedFormatId, &requestedFormatData);
2161 }
2162
2163 UINT rc = ERROR_INTERNAL_ERROR;
2164 if (res >= 0)
2165 {
2166 const CLIPRDR_FORMAT_DATA_RESPONSE response = {
2167 .common = { .msgType = CB_FORMAT_DATA_RESPONSE,
2168 .msgFlags = CB_RESPONSE_OK,
2169 .dataLen = (uint32_t)res },
2170 .requestedFormatData = requestedFormatData
2171 };
2172
2173 rc = clipboard->context->ClientFormatDataResponse(clipboard->context, &response);
2174 }
2175 else
2176 {
2177 const CLIPRDR_FORMAT_DATA_RESPONSE response = { .common = { .msgType =
2178 CB_FORMAT_DATA_RESPONSE,
2179 .msgFlags = CB_RESPONSE_FAIL,
2180 .dataLen = 0 },
2181 .requestedFormatData = NULL };
2182
2183 rc = clipboard->context->ClientFormatDataResponse(clipboard->context, &response);
2184 }
2185
2186 free(requestedFormatData);
2187 return rc;
2188}
2189
2195static UINT
2196wf_cliprdr_server_format_data_response(CliprdrClientContext* context,
2197 const CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse)
2198{
2199 BYTE* data;
2200 HANDLE hMem;
2201 wfClipboard* clipboard;
2202
2203 if (!context || !formatDataResponse)
2204 return ERROR_INTERNAL_ERROR;
2205
2206 clipboard = (wfClipboard*)context->custom;
2207
2208 if (!clipboard)
2209 return ERROR_INTERNAL_ERROR;
2210
2211 if (formatDataResponse->common.msgFlags != CB_RESPONSE_OK)
2212 {
2213 clipboard->hmem = NULL;
2214
2215 if (!SetEvent(clipboard->response_data_event))
2216 return ERROR_INTERNAL_ERROR;
2217
2218 return CHANNEL_RC_OK;
2219 }
2220
2221 hMem = GlobalAlloc(GMEM_MOVEABLE, formatDataResponse->common.dataLen);
2222
2223 if (!hMem)
2224 return ERROR_INTERNAL_ERROR;
2225
2226 data = (BYTE*)GlobalLock(hMem);
2227
2228 if (!data)
2229 {
2230 GlobalFree(hMem);
2231 return ERROR_INTERNAL_ERROR;
2232 }
2233
2234 CopyMemory(data, formatDataResponse->requestedFormatData, formatDataResponse->common.dataLen);
2235
2236 if (!GlobalUnlock(hMem) && GetLastError())
2237 {
2238 GlobalFree(hMem);
2239 return ERROR_INTERNAL_ERROR;
2240 }
2241
2242 clipboard->hmem = hMem;
2243
2244 if (!SetEvent(clipboard->response_data_event))
2245 return ERROR_INTERNAL_ERROR;
2246
2247 return CHANNEL_RC_OK;
2248}
2249
2255static UINT
2256wf_cliprdr_server_file_contents_request(CliprdrClientContext* context,
2257 const CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest)
2258{
2259 DWORD uSize = 0;
2260 BYTE* pData = NULL;
2261 HRESULT hRet = S_OK;
2262 FORMATETC vFormatEtc = { 0 };
2263 LPDATAOBJECT pDataObj = NULL;
2264 STGMEDIUM vStgMedium = { 0 };
2265 BOOL bIsStreamFile = TRUE;
2266 static LPSTREAM pStreamStc = NULL;
2267 static UINT32 uStreamIdStc = 0;
2268 wfClipboard* clipboard;
2269 UINT rc = ERROR_INTERNAL_ERROR;
2270 UINT sRc;
2271 UINT32 cbRequested;
2272
2273 if (!context || !fileContentsRequest)
2274 return ERROR_INTERNAL_ERROR;
2275
2276 clipboard = (wfClipboard*)context->custom;
2277
2278 if (!clipboard)
2279 return ERROR_INTERNAL_ERROR;
2280
2281 cbRequested = fileContentsRequest->cbRequested;
2282 if (fileContentsRequest->dwFlags == FILECONTENTS_SIZE)
2283 cbRequested = sizeof(UINT64);
2284
2285 pData = (BYTE*)calloc(1, cbRequested);
2286
2287 if (!pData)
2288 goto error;
2289
2290 hRet = OleGetClipboard(&pDataObj);
2291
2292 if (FAILED(hRet))
2293 {
2294 WLog_ERR(TAG, "filecontents: get ole clipboard failed.");
2295 goto error;
2296 }
2297
2298 vFormatEtc.cfFormat = RegisterClipboardFormat(CFSTR_FILECONTENTS);
2299 vFormatEtc.tymed = TYMED_ISTREAM;
2300 vFormatEtc.dwAspect = 1;
2301 vFormatEtc.lindex = fileContentsRequest->listIndex;
2302 vFormatEtc.ptd = NULL;
2303
2304 if ((uStreamIdStc != fileContentsRequest->streamId) || !pStreamStc)
2305 {
2306 LPENUMFORMATETC pEnumFormatEtc;
2307 ULONG CeltFetched;
2308 FORMATETC vFormatEtc2;
2309
2310 if (pStreamStc)
2311 {
2312 IStream_Release(pStreamStc);
2313 pStreamStc = NULL;
2314 }
2315
2316 bIsStreamFile = FALSE;
2317 hRet = IDataObject_EnumFormatEtc(pDataObj, DATADIR_GET, &pEnumFormatEtc);
2318
2319 if (hRet == S_OK)
2320 {
2321 do
2322 {
2323 hRet = IEnumFORMATETC_Next(pEnumFormatEtc, 1, &vFormatEtc2, &CeltFetched);
2324
2325 if (hRet == S_OK)
2326 {
2327 if (vFormatEtc2.cfFormat == RegisterClipboardFormat(CFSTR_FILECONTENTS))
2328 {
2329 hRet = IDataObject_GetData(pDataObj, &vFormatEtc, &vStgMedium);
2330
2331 if (hRet == S_OK)
2332 {
2333 pStreamStc = vStgMedium.u.pstm;
2334 uStreamIdStc = fileContentsRequest->streamId;
2335 bIsStreamFile = TRUE;
2336 }
2337
2338 break;
2339 }
2340 }
2341 } while (hRet == S_OK);
2342 }
2343 }
2344
2345 if (bIsStreamFile == TRUE)
2346 {
2347 if (fileContentsRequest->dwFlags == FILECONTENTS_SIZE)
2348 {
2349 STATSTG vStatStg = { 0 };
2350 hRet = IStream_Stat(pStreamStc, &vStatStg, STATFLAG_NONAME);
2351
2352 if (hRet == S_OK)
2353 {
2354 *((UINT32*)&pData[0]) = vStatStg.cbSize.QuadPart & 0xFFFFFFFF;
2355 *((UINT32*)&pData[4]) = (vStatStg.cbSize.QuadPart >> 32) & 0xFFFFFFFF;
2356 uSize = cbRequested;
2357 }
2358 }
2359 else if (fileContentsRequest->dwFlags == FILECONTENTS_RANGE)
2360 {
2361 LARGE_INTEGER dlibMove;
2362 ULARGE_INTEGER dlibNewPosition;
2363 dlibMove.QuadPart = (INT64)(((UINT64)fileContentsRequest->nPositionHigh << 32) |
2364 fileContentsRequest->nPositionLow);
2365 hRet = IStream_Seek(pStreamStc, dlibMove, STREAM_SEEK_SET, &dlibNewPosition);
2366
2367 if (SUCCEEDED(hRet))
2368 hRet = IStream_Read(pStreamStc, pData, cbRequested, (PULONG)&uSize);
2369 }
2370 }
2371 else
2372 {
2373 if (fileContentsRequest->dwFlags == FILECONTENTS_SIZE)
2374 {
2375 if (clipboard->nFiles <= fileContentsRequest->listIndex)
2376 goto error;
2377 *((UINT32*)&pData[0]) =
2378 clipboard->fileDescriptor[fileContentsRequest->listIndex]->nFileSizeLow;
2379 *((UINT32*)&pData[4]) =
2380 clipboard->fileDescriptor[fileContentsRequest->listIndex]->nFileSizeHigh;
2381 uSize = cbRequested;
2382 }
2383 else if (fileContentsRequest->dwFlags == FILECONTENTS_RANGE)
2384 {
2385 BOOL bRet;
2386 if (clipboard->nFiles <= fileContentsRequest->listIndex)
2387 goto error;
2388 bRet = wf_cliprdr_get_file_contents(
2389 clipboard->file_names[fileContentsRequest->listIndex], pData,
2390 fileContentsRequest->nPositionLow, fileContentsRequest->nPositionHigh, cbRequested,
2391 &uSize);
2392
2393 if (bRet == FALSE)
2394 {
2395 WLog_ERR(TAG, "get file contents failed.");
2396 uSize = 0;
2397 goto error;
2398 }
2399 }
2400 }
2401
2402 rc = CHANNEL_RC_OK;
2403error:
2404
2405 if (pDataObj)
2406 IDataObject_Release(pDataObj);
2407
2408 if (uSize == 0)
2409 {
2410 free(pData);
2411 pData = NULL;
2412 }
2413
2414 sRc =
2415 cliprdr_send_response_filecontents(clipboard, fileContentsRequest->streamId, uSize, pData);
2416 free(pData);
2417
2418 if (sRc != CHANNEL_RC_OK)
2419 return sRc;
2420
2421 return rc;
2422}
2423
2429static UINT
2430wf_cliprdr_server_file_contents_response(CliprdrClientContext* context,
2431 const CLIPRDR_FILE_CONTENTS_RESPONSE* fileContentsResponse)
2432{
2433 wfClipboard* clipboard;
2434
2435 if (!context || !fileContentsResponse)
2436 return ERROR_INTERNAL_ERROR;
2437
2438 if (fileContentsResponse->common.msgFlags != CB_RESPONSE_OK)
2439 return E_FAIL;
2440
2441 clipboard = (wfClipboard*)context->custom;
2442
2443 if (!clipboard)
2444 return ERROR_INTERNAL_ERROR;
2445
2446 clipboard->req_fsize = fileContentsResponse->cbRequested;
2447 clipboard->req_fdata = (char*)malloc(fileContentsResponse->cbRequested);
2448
2449 if (!clipboard->req_fdata)
2450 return ERROR_INTERNAL_ERROR;
2451
2452 CopyMemory(clipboard->req_fdata, fileContentsResponse->requestedData,
2453 fileContentsResponse->cbRequested);
2454
2455 if (!SetEvent(clipboard->req_fevent))
2456 {
2457 free(clipboard->req_fdata);
2458 return ERROR_INTERNAL_ERROR;
2459 }
2460
2461 return CHANNEL_RC_OK;
2462}
2463
2464BOOL wf_cliprdr_init(wfContext* wfc, CliprdrClientContext* cliprdr)
2465{
2466 wfClipboard* clipboard;
2467 rdpContext* context = (rdpContext*)wfc;
2468
2469 if (!context || !cliprdr)
2470 return FALSE;
2471
2472 wfc->clipboard = (wfClipboard*)calloc(1, sizeof(wfClipboard));
2473
2474 if (!wfc->clipboard)
2475 return FALSE;
2476
2477 clipboard = wfc->clipboard;
2478 clipboard->wfc = wfc;
2479 clipboard->context = cliprdr;
2480 clipboard->channels = context->channels;
2481 clipboard->sync = FALSE;
2482 clipboard->map_capacity = 32;
2483 clipboard->map_size = 0;
2484 clipboard->hUser32 = LoadLibraryA("user32.dll");
2485
2486 if (clipboard->hUser32)
2487 {
2488 clipboard->AddClipboardFormatListener = GetProcAddressAs(
2489 clipboard->hUser32, "AddClipboardFormatListener", fnAddClipboardFormatListener);
2490 clipboard->RemoveClipboardFormatListener = GetProcAddressAs(
2491 clipboard->hUser32, "RemoveClipboardFormatListener", fnRemoveClipboardFormatListener);
2492 clipboard->GetUpdatedClipboardFormats = GetProcAddressAs(
2493 clipboard->hUser32, "GetUpdatedClipboardFormats", fnGetUpdatedClipboardFormats);
2494 }
2495
2496 if (!(clipboard->hUser32 && clipboard->AddClipboardFormatListener &&
2497 clipboard->RemoveClipboardFormatListener && clipboard->GetUpdatedClipboardFormats))
2498 clipboard->legacyApi = TRUE;
2499
2500 if (!(clipboard->format_mappings =
2501 (formatMapping*)calloc(clipboard->map_capacity, sizeof(formatMapping))))
2502 goto error;
2503
2504 if (!(clipboard->response_data_event = CreateEvent(NULL, TRUE, FALSE, NULL)))
2505 goto error;
2506
2507 if (!(clipboard->req_fevent = CreateEvent(NULL, TRUE, FALSE, NULL)))
2508 goto error;
2509
2510 if (!(clipboard->thread = CreateThread(NULL, 0, cliprdr_thread_func, clipboard, 0, NULL)))
2511 goto error;
2512
2513 cliprdr->MonitorReady = wf_cliprdr_monitor_ready;
2514 cliprdr->ServerCapabilities = wf_cliprdr_server_capabilities;
2515 cliprdr->ServerFormatList = wf_cliprdr_server_format_list;
2516 cliprdr->ServerFormatListResponse = wf_cliprdr_server_format_list_response;
2517 cliprdr->ServerLockClipboardData = wf_cliprdr_server_lock_clipboard_data;
2518 cliprdr->ServerUnlockClipboardData = wf_cliprdr_server_unlock_clipboard_data;
2519 cliprdr->ServerFormatDataRequest = wf_cliprdr_server_format_data_request;
2520 cliprdr->ServerFormatDataResponse = wf_cliprdr_server_format_data_response;
2521 cliprdr->ServerFileContentsRequest = wf_cliprdr_server_file_contents_request;
2522 cliprdr->ServerFileContentsResponse = wf_cliprdr_server_file_contents_response;
2523 cliprdr->custom = (void*)wfc->clipboard;
2524 return TRUE;
2525error:
2526 wf_cliprdr_uninit(wfc, cliprdr);
2527 return FALSE;
2528}
2529
2530BOOL wf_cliprdr_uninit(wfContext* wfc, CliprdrClientContext* cliprdr)
2531{
2532 wfClipboard* clipboard;
2533
2534 if (!wfc || !cliprdr)
2535 return FALSE;
2536
2537 clipboard = wfc->clipboard;
2538
2539 if (!clipboard)
2540 return FALSE;
2541
2542 cliprdr->custom = NULL;
2543
2544 if (clipboard->hwnd)
2545 PostMessage(clipboard->hwnd, WM_QUIT, 0, 0);
2546
2547 if (clipboard->thread)
2548 {
2549 (void)WaitForSingleObject(clipboard->thread, INFINITE);
2550 (void)CloseHandle(clipboard->thread);
2551 }
2552
2553 if (clipboard->response_data_event)
2554 (void)CloseHandle(clipboard->response_data_event);
2555
2556 if (clipboard->req_fevent)
2557 (void)CloseHandle(clipboard->req_fevent);
2558
2559 clear_file_array(clipboard);
2560 clear_format_map(clipboard);
2561 free(clipboard->format_mappings);
2562 free(clipboard);
2563 return TRUE;
2564}