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, UINT64 position,
154  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.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 
262 static 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 
272 static 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 
312 static HRESULT STDMETHODCALLTYPE CliprdrStream_SetSize(IStream* This, ULARGE_INTEGER libNewSize)
313 {
314  (void)This;
315  (void)libNewSize;
316  return E_NOTIMPL;
317 }
318 
319 static 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 
331 static HRESULT STDMETHODCALLTYPE CliprdrStream_Commit(IStream* This, DWORD grfCommitFlags)
332 {
333  (void)This;
334  (void)grfCommitFlags;
335  return E_NOTIMPL;
336 }
337 
338 static HRESULT STDMETHODCALLTYPE CliprdrStream_Revert(IStream* This)
339 {
340  (void)This;
341  return E_NOTIMPL;
342 }
343 
344 static 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 
354 static 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 
364 static 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 
400 static HRESULT STDMETHODCALLTYPE CliprdrStream_Clone(IStream* This, IStream** ppstm)
401 {
402  (void)This;
403  (void)ppstm;
404  return E_NOTIMPL;
405 }
406 
407 static 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 
480 void CliprdrStream_Delete(CliprdrStream* instance)
481 {
482  if (instance)
483  {
484  free(instance->iStream.lpVtbl);
485  free(instance);
486  }
487 }
488 
493 static 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 
511 static 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 
532 static 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 
542 static 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 
561 static 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 
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] = { 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 
846 static void wf_destroy_file_obj(IDataObject* instance)
847 {
848  if (instance)
849  IDataObject_Release(instance);
850 }
851 
856 static 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 
869 static 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 
887 static 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 
897 static 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 
918 static 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 
938 static 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 
952 static 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 
963 static 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 
981 CliprdrEnumFORMATETC* 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;
1023 error:
1024  CliprdrEnumFORMATETC_Delete(instance);
1025  return NULL;
1026 }
1027 
1028 void 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 
1051 static 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 
1095 static INLINE BOOL file_transferring(wfClipboard* clipboard)
1096 {
1097  return get_local_format_id_by_name(clipboard, CFSTR_FILEDESCRIPTORW) ? TRUE : FALSE;
1098 }
1099 
1100 static 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 
1118 static 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 
1139 static 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 
1162 static 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 
1176 static 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 
1210 static 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 
1280 static 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 
1303 UINT 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 
1330 static 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 
1346 static 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 
1501 static 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 
1531 static 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 
1564 static 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 
1599 static 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;
1630 error:
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 */
1642 static 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 
1674 static 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 
1705 static 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 
1732 static 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 
1789 static 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 
1812 static 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 
1835 static 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 
1866 static 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 
1930 static UINT
1931 wf_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 
1948 static UINT
1949 wf_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 
1962 static UINT
1963 wf_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 
1971 static 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 
2004 static 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 
2033 unlock:
2034  GlobalUnlock(hClipdata);
2035 
2036 fail:
2037  CloseClipboard();
2038 
2039  return rc;
2040 }
2041 
2042 static 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 cchWideChar = MultiByteToWideChar(CP_ACP, MB_COMPOSITE, p, len, NULL, 0);
2100  WCHAR* wFileName = (LPWSTR)calloc(cchWideChar, sizeof(WCHAR));
2101  MultiByteToWideChar(CP_ACP, MB_COMPOSITE, p, len, wFileName, cchWideChar);
2102  wf_cliprdr_process_filename(clipboard, wFileName, cchWideChar);
2103  free(wFileName);
2104  }
2105  }
2106 
2107  GlobalUnlock(hdl);
2108  ReleaseStgMedium(&stg_medium);
2109 exit:
2110 {
2111  const size_t size = 4ull + clipboard->nFiles * sizeof(FILEDESCRIPTORW);
2112  FILEGROUPDESCRIPTORW* groupDsc = (FILEGROUPDESCRIPTORW*)calloc(size, 1);
2113 
2114  if (groupDsc)
2115  {
2116  groupDsc->cItems = clipboard->nFiles;
2117 
2118  for (size_t i = 0; i < clipboard->nFiles; i++)
2119  {
2120  if (clipboard->fileDescriptor[i])
2121  groupDsc->fgd[i] = *clipboard->fileDescriptor[i];
2122  }
2123 
2124  *pData = (BYTE*)groupDsc;
2125  rc = size;
2126  }
2127 }
2128 
2129  IDataObject_Release(dataObj);
2130  return rc;
2131 }
2132 
2138 static UINT
2139 wf_cliprdr_server_format_data_request(CliprdrClientContext* context,
2140  const CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest)
2141 {
2142  CLIPRDR_FORMAT_DATA_RESPONSE response = { 0 };
2143 
2144  if (!context || !formatDataRequest)
2145  return ERROR_INTERNAL_ERROR;
2146 
2147  wfClipboard* clipboard = (wfClipboard*)context->custom;
2148 
2149  if (!clipboard)
2150  return ERROR_INTERNAL_ERROR;
2151 
2152  const UINT32 requestedFormatId = formatDataRequest->requestedFormatId;
2153 
2154  if (requestedFormatId == RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW))
2155  {
2156  const SSIZE_T res = wf_cliprdr_get_filedescriptor(clipboard, &response.requestedFormatData);
2157  if (res > 0)
2158  response.common.dataLen = (UINT32)res;
2159  }
2160  else
2161  {
2162  const SSIZE_T res =
2163  wf_cliprdr_tryopen(clipboard, requestedFormatId, &response.requestedFormatData);
2164  if (res > 0)
2165  response.common.dataLen = (UINT32)res;
2166  }
2167 
2168  response.common.msgFlags = CB_RESPONSE_OK;
2169 
2170  const UINT rc = clipboard->context->ClientFormatDataResponse(clipboard->context, &response);
2171  free(response.requestedFormatData);
2172  return rc;
2173 }
2174 
2180 static UINT
2181 wf_cliprdr_server_format_data_response(CliprdrClientContext* context,
2182  const CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse)
2183 {
2184  BYTE* data;
2185  HANDLE hMem;
2186  wfClipboard* clipboard;
2187 
2188  if (!context || !formatDataResponse)
2189  return ERROR_INTERNAL_ERROR;
2190 
2191  clipboard = (wfClipboard*)context->custom;
2192 
2193  if (!clipboard)
2194  return ERROR_INTERNAL_ERROR;
2195 
2196  if (formatDataResponse->common.msgFlags != CB_RESPONSE_OK)
2197  {
2198  clipboard->hmem = NULL;
2199 
2200  if (!SetEvent(clipboard->response_data_event))
2201  return ERROR_INTERNAL_ERROR;
2202 
2203  return CHANNEL_RC_OK;
2204  }
2205 
2206  hMem = GlobalAlloc(GMEM_MOVEABLE, formatDataResponse->common.dataLen);
2207 
2208  if (!hMem)
2209  return ERROR_INTERNAL_ERROR;
2210 
2211  data = (BYTE*)GlobalLock(hMem);
2212 
2213  if (!data)
2214  {
2215  GlobalFree(hMem);
2216  return ERROR_INTERNAL_ERROR;
2217  }
2218 
2219  CopyMemory(data, formatDataResponse->requestedFormatData, formatDataResponse->common.dataLen);
2220 
2221  if (!GlobalUnlock(hMem) && GetLastError())
2222  {
2223  GlobalFree(hMem);
2224  return ERROR_INTERNAL_ERROR;
2225  }
2226 
2227  clipboard->hmem = hMem;
2228 
2229  if (!SetEvent(clipboard->response_data_event))
2230  return ERROR_INTERNAL_ERROR;
2231 
2232  return CHANNEL_RC_OK;
2233 }
2234 
2240 static UINT
2241 wf_cliprdr_server_file_contents_request(CliprdrClientContext* context,
2242  const CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest)
2243 {
2244  DWORD uSize = 0;
2245  BYTE* pData = NULL;
2246  HRESULT hRet = S_OK;
2247  FORMATETC vFormatEtc = { 0 };
2248  LPDATAOBJECT pDataObj = NULL;
2249  STGMEDIUM vStgMedium = { 0 };
2250  BOOL bIsStreamFile = TRUE;
2251  static LPSTREAM pStreamStc = NULL;
2252  static UINT32 uStreamIdStc = 0;
2253  wfClipboard* clipboard;
2254  UINT rc = ERROR_INTERNAL_ERROR;
2255  UINT sRc;
2256  UINT32 cbRequested;
2257 
2258  if (!context || !fileContentsRequest)
2259  return ERROR_INTERNAL_ERROR;
2260 
2261  clipboard = (wfClipboard*)context->custom;
2262 
2263  if (!clipboard)
2264  return ERROR_INTERNAL_ERROR;
2265 
2266  cbRequested = fileContentsRequest->cbRequested;
2267  if (fileContentsRequest->dwFlags == FILECONTENTS_SIZE)
2268  cbRequested = sizeof(UINT64);
2269 
2270  pData = (BYTE*)calloc(1, cbRequested);
2271 
2272  if (!pData)
2273  goto error;
2274 
2275  hRet = OleGetClipboard(&pDataObj);
2276 
2277  if (FAILED(hRet))
2278  {
2279  WLog_ERR(TAG, "filecontents: get ole clipboard failed.");
2280  goto error;
2281  }
2282 
2283  vFormatEtc.cfFormat = RegisterClipboardFormat(CFSTR_FILECONTENTS);
2284  vFormatEtc.tymed = TYMED_ISTREAM;
2285  vFormatEtc.dwAspect = 1;
2286  vFormatEtc.lindex = fileContentsRequest->listIndex;
2287  vFormatEtc.ptd = NULL;
2288 
2289  if ((uStreamIdStc != fileContentsRequest->streamId) || !pStreamStc)
2290  {
2291  LPENUMFORMATETC pEnumFormatEtc;
2292  ULONG CeltFetched;
2293  FORMATETC vFormatEtc2;
2294 
2295  if (pStreamStc)
2296  {
2297  IStream_Release(pStreamStc);
2298  pStreamStc = NULL;
2299  }
2300 
2301  bIsStreamFile = FALSE;
2302  hRet = IDataObject_EnumFormatEtc(pDataObj, DATADIR_GET, &pEnumFormatEtc);
2303 
2304  if (hRet == S_OK)
2305  {
2306  do
2307  {
2308  hRet = IEnumFORMATETC_Next(pEnumFormatEtc, 1, &vFormatEtc2, &CeltFetched);
2309 
2310  if (hRet == S_OK)
2311  {
2312  if (vFormatEtc2.cfFormat == RegisterClipboardFormat(CFSTR_FILECONTENTS))
2313  {
2314  hRet = IDataObject_GetData(pDataObj, &vFormatEtc, &vStgMedium);
2315 
2316  if (hRet == S_OK)
2317  {
2318  pStreamStc = vStgMedium.u.pstm;
2319  uStreamIdStc = fileContentsRequest->streamId;
2320  bIsStreamFile = TRUE;
2321  }
2322 
2323  break;
2324  }
2325  }
2326  } while (hRet == S_OK);
2327  }
2328  }
2329 
2330  if (bIsStreamFile == TRUE)
2331  {
2332  if (fileContentsRequest->dwFlags == FILECONTENTS_SIZE)
2333  {
2334  STATSTG vStatStg = { 0 };
2335  hRet = IStream_Stat(pStreamStc, &vStatStg, STATFLAG_NONAME);
2336 
2337  if (hRet == S_OK)
2338  {
2339  *((UINT32*)&pData[0]) = vStatStg.cbSize.QuadPart & 0xFFFFFFFF;
2340  *((UINT32*)&pData[4]) = (vStatStg.cbSize.QuadPart >> 32) & 0xFFFFFFFF;
2341  uSize = cbRequested;
2342  }
2343  }
2344  else if (fileContentsRequest->dwFlags == FILECONTENTS_RANGE)
2345  {
2346  LARGE_INTEGER dlibMove;
2347  ULARGE_INTEGER dlibNewPosition;
2348  dlibMove.QuadPart = (INT64)(((UINT64)fileContentsRequest->nPositionHigh << 32) |
2349  fileContentsRequest->nPositionLow);
2350  hRet = IStream_Seek(pStreamStc, dlibMove, STREAM_SEEK_SET, &dlibNewPosition);
2351 
2352  if (SUCCEEDED(hRet))
2353  hRet = IStream_Read(pStreamStc, pData, cbRequested, (PULONG)&uSize);
2354  }
2355  }
2356  else
2357  {
2358  if (fileContentsRequest->dwFlags == FILECONTENTS_SIZE)
2359  {
2360  if (clipboard->nFiles <= fileContentsRequest->listIndex)
2361  goto error;
2362  *((UINT32*)&pData[0]) =
2363  clipboard->fileDescriptor[fileContentsRequest->listIndex]->nFileSizeLow;
2364  *((UINT32*)&pData[4]) =
2365  clipboard->fileDescriptor[fileContentsRequest->listIndex]->nFileSizeHigh;
2366  uSize = cbRequested;
2367  }
2368  else if (fileContentsRequest->dwFlags == FILECONTENTS_RANGE)
2369  {
2370  BOOL bRet;
2371  if (clipboard->nFiles <= fileContentsRequest->listIndex)
2372  goto error;
2373  bRet = wf_cliprdr_get_file_contents(
2374  clipboard->file_names[fileContentsRequest->listIndex], pData,
2375  fileContentsRequest->nPositionLow, fileContentsRequest->nPositionHigh, cbRequested,
2376  &uSize);
2377 
2378  if (bRet == FALSE)
2379  {
2380  WLog_ERR(TAG, "get file contents failed.");
2381  uSize = 0;
2382  goto error;
2383  }
2384  }
2385  }
2386 
2387  rc = CHANNEL_RC_OK;
2388 error:
2389 
2390  if (pDataObj)
2391  IDataObject_Release(pDataObj);
2392 
2393  if (uSize == 0)
2394  {
2395  free(pData);
2396  pData = NULL;
2397  }
2398 
2399  sRc =
2400  cliprdr_send_response_filecontents(clipboard, fileContentsRequest->streamId, uSize, pData);
2401  free(pData);
2402 
2403  if (sRc != CHANNEL_RC_OK)
2404  return sRc;
2405 
2406  return rc;
2407 }
2408 
2414 static UINT
2415 wf_cliprdr_server_file_contents_response(CliprdrClientContext* context,
2416  const CLIPRDR_FILE_CONTENTS_RESPONSE* fileContentsResponse)
2417 {
2418  wfClipboard* clipboard;
2419 
2420  if (!context || !fileContentsResponse)
2421  return ERROR_INTERNAL_ERROR;
2422 
2423  if (fileContentsResponse->common.msgFlags != CB_RESPONSE_OK)
2424  return E_FAIL;
2425 
2426  clipboard = (wfClipboard*)context->custom;
2427 
2428  if (!clipboard)
2429  return ERROR_INTERNAL_ERROR;
2430 
2431  clipboard->req_fsize = fileContentsResponse->cbRequested;
2432  clipboard->req_fdata = (char*)malloc(fileContentsResponse->cbRequested);
2433 
2434  if (!clipboard->req_fdata)
2435  return ERROR_INTERNAL_ERROR;
2436 
2437  CopyMemory(clipboard->req_fdata, fileContentsResponse->requestedData,
2438  fileContentsResponse->cbRequested);
2439 
2440  if (!SetEvent(clipboard->req_fevent))
2441  {
2442  free(clipboard->req_fdata);
2443  return ERROR_INTERNAL_ERROR;
2444  }
2445 
2446  return CHANNEL_RC_OK;
2447 }
2448 
2449 BOOL wf_cliprdr_init(wfContext* wfc, CliprdrClientContext* cliprdr)
2450 {
2451  wfClipboard* clipboard;
2452  rdpContext* context = (rdpContext*)wfc;
2453 
2454  if (!context || !cliprdr)
2455  return FALSE;
2456 
2457  wfc->clipboard = (wfClipboard*)calloc(1, sizeof(wfClipboard));
2458 
2459  if (!wfc->clipboard)
2460  return FALSE;
2461 
2462  clipboard = wfc->clipboard;
2463  clipboard->wfc = wfc;
2464  clipboard->context = cliprdr;
2465  clipboard->channels = context->channels;
2466  clipboard->sync = FALSE;
2467  clipboard->map_capacity = 32;
2468  clipboard->map_size = 0;
2469  clipboard->hUser32 = LoadLibraryA("user32.dll");
2470 
2471  if (clipboard->hUser32)
2472  {
2473  clipboard->AddClipboardFormatListener = GetProcAddressAs(
2474  clipboard->hUser32, "AddClipboardFormatListener", fnAddClipboardFormatListener);
2475  clipboard->RemoveClipboardFormatListener = GetProcAddressAs(
2476  clipboard->hUser32, "RemoveClipboardFormatListener", fnRemoveClipboardFormatListener);
2477  clipboard->GetUpdatedClipboardFormats = GetProcAddressAs(
2478  clipboard->hUser32, "GetUpdatedClipboardFormats", fnGetUpdatedClipboardFormats);
2479  }
2480 
2481  if (!(clipboard->hUser32 && clipboard->AddClipboardFormatListener &&
2482  clipboard->RemoveClipboardFormatListener && clipboard->GetUpdatedClipboardFormats))
2483  clipboard->legacyApi = TRUE;
2484 
2485  if (!(clipboard->format_mappings =
2486  (formatMapping*)calloc(clipboard->map_capacity, sizeof(formatMapping))))
2487  goto error;
2488 
2489  if (!(clipboard->response_data_event = CreateEvent(NULL, TRUE, FALSE, NULL)))
2490  goto error;
2491 
2492  if (!(clipboard->req_fevent = CreateEvent(NULL, TRUE, FALSE, NULL)))
2493  goto error;
2494 
2495  if (!(clipboard->thread = CreateThread(NULL, 0, cliprdr_thread_func, clipboard, 0, NULL)))
2496  goto error;
2497 
2498  cliprdr->MonitorReady = wf_cliprdr_monitor_ready;
2499  cliprdr->ServerCapabilities = wf_cliprdr_server_capabilities;
2500  cliprdr->ServerFormatList = wf_cliprdr_server_format_list;
2501  cliprdr->ServerFormatListResponse = wf_cliprdr_server_format_list_response;
2502  cliprdr->ServerLockClipboardData = wf_cliprdr_server_lock_clipboard_data;
2503  cliprdr->ServerUnlockClipboardData = wf_cliprdr_server_unlock_clipboard_data;
2504  cliprdr->ServerFormatDataRequest = wf_cliprdr_server_format_data_request;
2505  cliprdr->ServerFormatDataResponse = wf_cliprdr_server_format_data_response;
2506  cliprdr->ServerFileContentsRequest = wf_cliprdr_server_file_contents_request;
2507  cliprdr->ServerFileContentsResponse = wf_cliprdr_server_file_contents_response;
2508  cliprdr->custom = (void*)wfc->clipboard;
2509  return TRUE;
2510 error:
2511  wf_cliprdr_uninit(wfc, cliprdr);
2512  return FALSE;
2513 }
2514 
2515 BOOL wf_cliprdr_uninit(wfContext* wfc, CliprdrClientContext* cliprdr)
2516 {
2517  wfClipboard* clipboard;
2518 
2519  if (!wfc || !cliprdr)
2520  return FALSE;
2521 
2522  clipboard = wfc->clipboard;
2523 
2524  if (!clipboard)
2525  return FALSE;
2526 
2527  cliprdr->custom = NULL;
2528 
2529  if (clipboard->hwnd)
2530  PostMessage(clipboard->hwnd, WM_QUIT, 0, 0);
2531 
2532  if (clipboard->thread)
2533  {
2534  (void)WaitForSingleObject(clipboard->thread, INFINITE);
2535  (void)CloseHandle(clipboard->thread);
2536  }
2537 
2538  if (clipboard->response_data_event)
2539  (void)CloseHandle(clipboard->response_data_event);
2540 
2541  if (clipboard->req_fevent)
2542  (void)CloseHandle(clipboard->req_fevent);
2543 
2544  clear_file_array(clipboard);
2545  clear_format_map(clipboard);
2546  free(clipboard->format_mappings);
2547  free(clipboard);
2548  return TRUE;
2549 }