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