setupapi: Improve parameter validation for SetupCreateDiskSpaceListA/W.
[wine] / dlls / winex11.drv / xdnd.c
1 /*
2  * XDND handler code
3  *
4  * Copyright 2003 Ulrich Czekalla
5  * Copyright 2007 Damjan Jovanovic
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <string.h>
26 #ifdef HAVE_UNISTD_H
27 # include <unistd.h>
28 #endif
29 #include <stdarg.h>
30 #include <stdio.h>
31
32 #define NONAMELESSUNION
33
34 #include "windef.h"
35 #include "winbase.h"
36 #include "wingdi.h"
37 #include "winuser.h"
38
39 #define COBJMACROS
40 #include "x11drv.h"
41 #include "shlobj.h"  /* DROPFILES */
42 #include "oleidl.h"
43 #include "objidl.h"
44
45 #include "wine/unicode.h"
46 #include "wine/debug.h"
47 #include "wine/list.h"
48
49 WINE_DEFAULT_DEBUG_CHANNEL(xdnd);
50
51 /* Maximum wait time for selection notify */
52 #define SELECTION_RETRIES 500  /* wait for .1 seconds */
53 #define SELECTION_WAIT    1000 /* us */
54
55 typedef struct tagXDNDDATA
56 {
57     int cf_win;
58     Atom cf_xdnd;
59     void *data;
60     unsigned int size;
61     struct list entry;
62 } XDNDDATA, *LPXDNDDATA;
63
64 static struct list xdndData = LIST_INIT(xdndData);
65 static POINT XDNDxy = { 0, 0 };
66 static IDataObject XDNDDataObject;
67 static BOOL XDNDAccepted = FALSE;
68 static DWORD XDNDDropEffect = DROPEFFECT_NONE;
69 /* the last window the mouse was over */
70 static HWND XDNDLastTargetWnd;
71 /* might be a ancestor of XDNDLastTargetWnd */
72 static HWND XDNDLastDropTargetWnd;
73
74 static void X11DRV_XDND_InsertXDNDData(int property, int format, void* data, unsigned int len);
75 static int X11DRV_XDND_DeconstructTextURIList(int property, void* data, int len);
76 static int X11DRV_XDND_DeconstructTextPlain(int property, void* data, int len);
77 static int X11DRV_XDND_DeconstructTextHTML(int property, void* data, int len);
78 static int X11DRV_XDND_MapFormat(unsigned int property, unsigned char *data, int len);
79 static void X11DRV_XDND_ResolveProperty(Display *display, Window xwin, Time tm,
80     Atom *types, unsigned long *count);
81 static void X11DRV_XDND_SendDropFiles(HWND hwnd);
82 static void X11DRV_XDND_FreeDragDropOp(void);
83 static unsigned int X11DRV_XDND_UnixToDos(char** lpdest, char* lpsrc, int len);
84 static WCHAR* X11DRV_XDND_URIToDOS(char *encodedURI);
85
86 static CRITICAL_SECTION xdnd_cs;
87 static CRITICAL_SECTION_DEBUG critsect_debug =
88 {
89     0, 0, &xdnd_cs,
90     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
91       0, 0, { (DWORD_PTR)(__FILE__ ": xdnd_cs") }
92 };
93 static CRITICAL_SECTION xdnd_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
94
95
96 /* Based on functions in dlls/ole32/ole2.c */
97 static HANDLE get_droptarget_local_handle(HWND hwnd)
98 {
99     static const WCHAR prop_marshalleddroptarget[] =
100         {'W','i','n','e','M','a','r','s','h','a','l','l','e','d','D','r','o','p','T','a','r','g','e','t',0};
101     HANDLE handle;
102     HANDLE local_handle = 0;
103
104     handle = GetPropW(hwnd, prop_marshalleddroptarget);
105     if (handle)
106     {
107         DWORD pid;
108         HANDLE process;
109
110         GetWindowThreadProcessId(hwnd, &pid);
111         process = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid);
112         if (process)
113         {
114             DuplicateHandle(process, handle, GetCurrentProcess(), &local_handle, 0, FALSE, DUPLICATE_SAME_ACCESS);
115             CloseHandle(process);
116         }
117     }
118     return local_handle;
119 }
120
121 static HRESULT create_stream_from_map(HANDLE map, IStream **stream)
122 {
123     HRESULT hr = E_OUTOFMEMORY;
124     HGLOBAL hmem;
125     void *data;
126     MEMORY_BASIC_INFORMATION info;
127
128     data = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
129     if(!data) return hr;
130
131     VirtualQuery(data, &info, sizeof(info));
132     TRACE("size %d\n", (int)info.RegionSize);
133
134     hmem = GlobalAlloc(GMEM_MOVEABLE, info.RegionSize);
135     if(hmem)
136     {
137         memcpy(GlobalLock(hmem), data, info.RegionSize);
138         GlobalUnlock(hmem);
139         hr = CreateStreamOnHGlobal(hmem, TRUE, stream);
140     }
141     UnmapViewOfFile(data);
142     return hr;
143 }
144
145 static IDropTarget* get_droptarget_pointer(HWND hwnd)
146 {
147     IDropTarget *droptarget = NULL;
148     HANDLE map;
149     IStream *stream;
150
151     map = get_droptarget_local_handle(hwnd);
152     if(!map) return NULL;
153
154     if(SUCCEEDED(create_stream_from_map(map, &stream)))
155     {
156         CoUnmarshalInterface(stream, &IID_IDropTarget, (void**)&droptarget);
157         IStream_Release(stream);
158     }
159     CloseHandle(map);
160     return droptarget;
161 }
162
163 /**************************************************************************
164  * X11DRV_XDND_XdndActionToDROPEFFECT
165  */
166 static DWORD X11DRV_XDND_XdndActionToDROPEFFECT(long action)
167 {
168     /* In Windows, nothing but the given effects is allowed.
169      * In X the given action is just a hint, and you can always
170      * XdndActionCopy and XdndActionPrivate, so be more permissive. */
171     if (action == x11drv_atom(XdndActionCopy))
172         return DROPEFFECT_COPY;
173     else if (action == x11drv_atom(XdndActionMove))
174         return DROPEFFECT_MOVE | DROPEFFECT_COPY;
175     else if (action == x11drv_atom(XdndActionLink))
176         return DROPEFFECT_LINK | DROPEFFECT_COPY;
177     else if (action == x11drv_atom(XdndActionAsk))
178         /* FIXME: should we somehow ask the user what to do here? */
179         return DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK;
180     FIXME("unknown action %ld, assuming DROPEFFECT_COPY\n", action);
181     return DROPEFFECT_COPY;
182 }
183
184 /**************************************************************************
185  * X11DRV_XDND_DROPEFFECTToXdndAction
186  */
187 static long X11DRV_XDND_DROPEFFECTToXdndAction(DWORD effect)
188 {
189     if (effect == DROPEFFECT_COPY)
190         return x11drv_atom(XdndActionCopy);
191     else if (effect == DROPEFFECT_MOVE)
192         return x11drv_atom(XdndActionMove);
193     else if (effect == DROPEFFECT_LINK)
194         return x11drv_atom(XdndActionLink);
195     FIXME("unknown drop effect %u, assuming XdndActionCopy\n", effect);
196     return x11drv_atom(XdndActionCopy);
197 }
198
199 /**************************************************************************
200  * X11DRV_XDND_EnterEvent
201  *
202  * Handle an XdndEnter event.
203  */
204 void X11DRV_XDND_EnterEvent( HWND hWnd, XClientMessageEvent *event )
205 {
206     int version;
207     Atom *xdndtypes;
208     unsigned long count = 0;
209
210     version = (event->data.l[1] & 0xFF000000) >> 24;
211     TRACE("ver(%d) check-XdndTypeList(%ld) data=%ld,%ld,%ld,%ld,%ld\n",
212           version, (event->data.l[1] & 1),
213           event->data.l[0], event->data.l[1], event->data.l[2],
214           event->data.l[3], event->data.l[4]);
215
216     if (version > WINE_XDND_VERSION)
217     {
218         TRACE("Ignores unsupported version\n");
219         return;
220     }
221
222     XDNDAccepted = FALSE;
223
224     /* If the source supports more than 3 data types we retrieve
225      * the entire list. */
226     if (event->data.l[1] & 1)
227     {
228         Atom acttype;
229         int actfmt;
230         unsigned long bytesret;
231
232         /* Request supported formats from source window */
233         wine_tsx11_lock();
234         XGetWindowProperty(event->display, event->data.l[0], x11drv_atom(XdndTypeList),
235                            0, 65535, FALSE, AnyPropertyType, &acttype, &actfmt, &count,
236                            &bytesret, (unsigned char**)&xdndtypes);
237         wine_tsx11_unlock();
238     }
239     else
240     {
241         count = 3;
242         xdndtypes = (Atom*) &event->data.l[2];
243     }
244
245     if (TRACE_ON(xdnd))
246     {
247         unsigned int i = 0;
248
249         wine_tsx11_lock();
250         for (; i < count; i++)
251         {
252             if (xdndtypes[i] != 0)
253             {
254                 char * pn = XGetAtomName(event->display, xdndtypes[i]);
255                 TRACE("XDNDEnterAtom %ld: %s\n", xdndtypes[i], pn);
256                 XFree(pn);
257             }
258         }
259         wine_tsx11_unlock();
260     }
261
262     /* Do a one-time data read and cache results */
263     X11DRV_XDND_ResolveProperty(event->display, event->window,
264                                 event->data.l[1], xdndtypes, &count);
265
266     if (event->data.l[1] & 1)
267         XFree(xdndtypes);
268 }
269
270 /**************************************************************************
271  * X11DRV_XDND_PositionEvent
272  *
273  * Handle an XdndPosition event.
274  */
275 void X11DRV_XDND_PositionEvent( HWND hWnd, XClientMessageEvent *event )
276 {
277     XClientMessageEvent e;
278     int accept = 0; /* Assume we're not accepting */
279     IDropTarget *dropTarget = NULL;
280     DWORD effect;
281     POINTL pointl;
282     HWND targetWindow;
283     HRESULT hr;
284
285     XDNDxy.x = event->data.l[2] >> 16;
286     XDNDxy.y = event->data.l[2] & 0xFFFF;
287     targetWindow = WindowFromPoint(XDNDxy);
288
289     pointl.x = XDNDxy.x;
290     pointl.y = XDNDxy.y;
291     effect = X11DRV_XDND_XdndActionToDROPEFFECT(event->data.l[4]);
292
293     if (!XDNDAccepted || XDNDLastTargetWnd != targetWindow)
294     {
295         /* Notify OLE of DragEnter. Result determines if we accept */
296         HWND dropTargetWindow;
297
298         if (XDNDLastDropTargetWnd)
299         {
300             dropTarget = get_droptarget_pointer(XDNDLastDropTargetWnd);
301             if (dropTarget)
302             {
303                 hr = IDropTarget_DragLeave(dropTarget);
304                 if (FAILED(hr))
305                     WARN("IDropTarget_DragLeave failed, error 0x%08X\n", hr);
306                 IDropTarget_Release(dropTarget);
307             }
308         }
309         dropTargetWindow = targetWindow;
310         do
311         {
312             dropTarget = get_droptarget_pointer(dropTargetWindow);
313         } while (dropTarget == NULL && (dropTargetWindow = GetParent(dropTargetWindow)) != NULL);
314         XDNDLastTargetWnd = targetWindow;
315         XDNDLastDropTargetWnd = dropTargetWindow;
316         if (dropTarget)
317         {
318             hr = IDropTarget_DragEnter(dropTarget, &XDNDDataObject,
319                                        MK_LBUTTON, pointl, &effect);
320             if (SUCCEEDED(hr))
321             {
322                 if (effect != DROPEFFECT_NONE)
323                 {
324                     XDNDAccepted = TRUE;
325                     TRACE("the application accepted the drop\n");
326                 }
327                 else
328                     TRACE("the application refused the drop\n");
329             }
330             else
331                 WARN("IDropTarget_DragEnter failed, error 0x%08X\n", hr);
332             IDropTarget_Release(dropTarget);
333         }
334     }
335     if (XDNDAccepted && XDNDLastTargetWnd == targetWindow)
336     {
337         /* If drag accepted notify OLE of DragOver */
338         dropTarget = get_droptarget_pointer(XDNDLastDropTargetWnd);
339         if (dropTarget)
340         {
341             hr = IDropTarget_DragOver(dropTarget, MK_LBUTTON, pointl, &effect);
342             if (SUCCEEDED(hr))
343                 XDNDDropEffect = effect;
344             else
345                 WARN("IDropTarget_DragOver failed, error 0x%08X\n", hr);
346             IDropTarget_Release(dropTarget);
347         }
348     }
349
350     if (XDNDAccepted)
351         accept = 1;
352     if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)
353         accept = 1;
354
355     TRACE("action req: %ld accept(%d) at x(%d),y(%d)\n",
356           event->data.l[4], accept, XDNDxy.x, XDNDxy.y);
357
358     /*
359      * Let source know if we're accepting the drop by
360      * sending a status message.
361      */
362     e.type = ClientMessage;
363     e.display = event->display;
364     e.window = event->data.l[0];
365     e.message_type = x11drv_atom(XdndStatus);
366     e.format = 32;
367     e.data.l[0] = event->window;
368     e.data.l[1] = accept;
369     e.data.l[2] = 0; /* Empty Rect */
370     e.data.l[3] = 0; /* Empty Rect */
371     if (accept)
372         e.data.l[4] = X11DRV_XDND_DROPEFFECTToXdndAction(effect);
373     else
374         e.data.l[4] = None;
375     wine_tsx11_lock();
376     XSendEvent(event->display, event->data.l[0], False, NoEventMask, (XEvent*)&e);
377     wine_tsx11_unlock();
378 }
379
380 /**************************************************************************
381  * X11DRV_XDND_DropEvent
382  *
383  * Handle an XdndDrop event.
384  */
385 void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event )
386 {
387     XClientMessageEvent e;
388     IDropTarget *dropTarget;
389
390     TRACE("\n");
391
392     /* If we have a HDROP type we send a WM_ACCEPTFILES.*/
393     if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)
394         X11DRV_XDND_SendDropFiles( hWnd );
395
396     /* Notify OLE of Drop */
397     dropTarget = get_droptarget_pointer(XDNDLastDropTargetWnd);
398     if (dropTarget)
399     {
400         HRESULT hr;
401         POINTL pointl;
402         DWORD effect = XDNDDropEffect;
403
404         pointl.x = XDNDxy.x;
405         pointl.y = XDNDxy.y;
406         hr = IDropTarget_Drop(dropTarget, &XDNDDataObject, MK_LBUTTON,
407                               pointl, &effect);
408         if (SUCCEEDED(hr))
409         {
410             if (effect != DROPEFFECT_NONE)
411                 TRACE("drop succeeded\n");
412             else
413                 TRACE("the application refused the drop\n");
414         }
415         else
416             WARN("drop failed, error 0x%08X\n", hr);
417         IDropTarget_Release(dropTarget);
418     }
419
420     X11DRV_XDND_FreeDragDropOp();
421
422     /* Tell the target we are finished. */
423     memset(&e, 0, sizeof(e));
424     e.type = ClientMessage;
425     e.display = event->display;
426     e.window = event->data.l[0];
427     e.message_type = x11drv_atom(XdndFinished);
428     e.format = 32;
429     e.data.l[0] = event->window;
430     wine_tsx11_lock();
431     XSendEvent(event->display, event->data.l[0], False, NoEventMask, (XEvent*)&e);
432     wine_tsx11_unlock();
433 }
434
435 /**************************************************************************
436  * X11DRV_XDND_LeaveEvent
437  *
438  * Handle an XdndLeave event.
439  */
440 void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event )
441 {
442     IDropTarget *dropTarget;
443
444     TRACE("DND Operation canceled\n");
445
446     /* Notify OLE of DragLeave */
447     dropTarget = get_droptarget_pointer(XDNDLastDropTargetWnd);
448     if (dropTarget)
449     {
450         HRESULT hr = IDropTarget_DragLeave(dropTarget);
451         if (FAILED(hr))
452             WARN("IDropTarget_DragLeave failed, error 0x%08X\n", hr);
453         IDropTarget_Release(dropTarget);
454     }
455
456     X11DRV_XDND_FreeDragDropOp();
457 }
458
459
460 /**************************************************************************
461  * X11DRV_XDND_ResolveProperty
462  *
463  * Resolve all MIME types to windows clipboard formats. All data is cached.
464  */
465 static void X11DRV_XDND_ResolveProperty(Display *display, Window xwin, Time tm,
466                                         Atom *types, unsigned long *count)
467 {
468     unsigned int i, j;
469     BOOL res;
470     XEvent xe;
471     Atom acttype;
472     int actfmt;
473     unsigned long bytesret, icount;
474     int entries = 0;
475     unsigned char* data = NULL;
476     XDNDDATA *current, *next;
477     BOOL haveHDROP = FALSE;
478
479     TRACE("count(%ld)\n", *count);
480
481     X11DRV_XDND_FreeDragDropOp(); /* Clear previously cached data */
482
483     for (i = 0; i < *count; i++)
484     {
485         TRACE("requesting atom %ld from xwin %ld\n", types[i], xwin);
486
487         if (types[i] == 0)
488             continue;
489
490         wine_tsx11_lock();
491         XConvertSelection(display, x11drv_atom(XdndSelection), types[i],
492                           x11drv_atom(XdndTarget), xwin, /*tm*/CurrentTime);
493         wine_tsx11_unlock();
494
495         /*
496          * Wait for SelectionNotify
497          */
498         for (j = 0; j < SELECTION_RETRIES; j++)
499         {
500             wine_tsx11_lock();
501             res = XCheckTypedWindowEvent(display, xwin, SelectionNotify, &xe);
502             wine_tsx11_unlock();
503             if (res && xe.xselection.selection == x11drv_atom(XdndSelection)) break;
504
505             usleep(SELECTION_WAIT);
506         }
507
508         if (xe.xselection.property == None)
509             continue;
510
511         wine_tsx11_lock();
512         XGetWindowProperty(display, xwin, x11drv_atom(XdndTarget), 0, 65535, FALSE,
513             AnyPropertyType, &acttype, &actfmt, &icount, &bytesret, &data);
514         wine_tsx11_unlock();
515
516         entries += X11DRV_XDND_MapFormat(types[i], data, get_property_size( actfmt, icount ));
517         wine_tsx11_lock();
518         XFree(data);
519         wine_tsx11_unlock();
520     }
521
522     /* On Windows when there is a CF_HDROP, there are no other CF_ formats.
523      * foobar2000 relies on this (spaces -> %20's without it).
524      */
525     LIST_FOR_EACH_ENTRY(current, &xdndData, XDNDDATA, entry)
526     {
527         if (current->cf_win == CF_HDROP)
528         {
529             haveHDROP = TRUE;
530             break;
531         }
532     }
533     if (haveHDROP)
534     {
535         LIST_FOR_EACH_ENTRY_SAFE(current, next, &xdndData, XDNDDATA, entry)
536         {
537             if (current->cf_win != CF_HDROP && current->cf_win < CF_MAX)
538             {
539                 list_remove(&current->entry);
540                 HeapFree(GetProcessHeap(), 0, current->data);
541                 HeapFree(GetProcessHeap(), 0, current);
542                 --entries;
543             }
544         }
545     }
546
547     *count = entries;
548 }
549
550
551 /**************************************************************************
552  * X11DRV_XDND_InsertXDNDData
553  *
554  * Cache available XDND property
555  */
556 static void X11DRV_XDND_InsertXDNDData(int property, int format, void* data, unsigned int len)
557 {
558     LPXDNDDATA current = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(XDNDDATA));
559
560     if (current)
561     {
562         EnterCriticalSection(&xdnd_cs);
563         current->cf_xdnd = property;
564         current->cf_win = format;
565         current->data = data;
566         current->size = len;
567         list_add_tail(&xdndData, &current->entry);
568         LeaveCriticalSection(&xdnd_cs);
569     }
570 }
571
572
573 /**************************************************************************
574  * X11DRV_XDND_MapFormat
575  *
576  * Map XDND MIME format to windows clipboard format.
577  */
578 static int X11DRV_XDND_MapFormat(unsigned int property, unsigned char *data, int len)
579 {
580     void* xdata;
581     int count = 0;
582
583     TRACE("%d: %s\n", property, data);
584
585     /* Always include the raw type */
586     xdata = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len);
587     memcpy(xdata, data, len);
588     X11DRV_XDND_InsertXDNDData(property, property, xdata, len);
589     count++;
590
591     if (property == x11drv_atom(text_uri_list))
592         count += X11DRV_XDND_DeconstructTextURIList(property, data, len);
593     else if (property == x11drv_atom(text_plain))
594         count += X11DRV_XDND_DeconstructTextPlain(property, data, len);
595     else if (property == x11drv_atom(text_html))
596         count += X11DRV_XDND_DeconstructTextHTML(property, data, len);
597
598     return count;
599 }
600
601
602 /**************************************************************************
603  * X11DRV_XDND_DeconstructTextURIList
604  *
605  * Interpret text/uri-list data and add records to <dndfmt> linked list
606  */
607 static int X11DRV_XDND_DeconstructTextURIList(int property, void* data, int len)
608 {
609     char *uriList = data;
610     char *uri;
611     WCHAR *path;
612
613     WCHAR *out = NULL;
614     int size = 0;
615     int capacity = 4096;
616
617     int count = 0;
618     int start = 0;
619     int end = 0;
620
621     out = HeapAlloc(GetProcessHeap(), 0, capacity * sizeof(WCHAR));
622     if (out == NULL)
623         return 0;
624
625     while (end < len)
626     {
627         while (end < len && uriList[end] != '\r')
628             ++end;
629         if (end < (len - 1) && uriList[end+1] != '\n')
630         {
631             WARN("URI list line doesn't end in \\r\\n\n");
632             break;
633         }
634
635         uri = HeapAlloc(GetProcessHeap(), 0, end - start + 1);
636         if (uri == NULL)
637             break;
638         lstrcpynA(uri, &uriList[start], end - start + 1);
639         path = X11DRV_XDND_URIToDOS(uri);
640         TRACE("converted URI %s to DOS path %s\n", debugstr_a(uri), debugstr_w(path));
641         HeapFree(GetProcessHeap(), 0, uri);
642
643         if (path)
644         {
645             int pathSize = strlenW(path) + 1;
646             if (pathSize > capacity-size)
647             {
648                 capacity = 2*capacity + pathSize;
649                 out = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, out, (capacity + 1)*sizeof(WCHAR));
650                 if (out == NULL)
651                     goto done;
652             }
653             memcpy(&out[size], path, pathSize * sizeof(WCHAR));
654             size += pathSize;
655         done:
656             HeapFree(GetProcessHeap(), 0, path);
657             if (out == NULL)
658                 break;
659         }
660
661         start = end + 2;
662         end = start;
663     }
664     if (out && end >= len)
665     {
666         DROPFILES *dropFiles;
667         dropFiles = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DROPFILES) + (size + 1)*sizeof(WCHAR));
668         if (dropFiles)
669         {
670             dropFiles->pFiles = sizeof(DROPFILES);
671             dropFiles->pt.x = XDNDxy.x;
672             dropFiles->pt.y = XDNDxy.y;
673             dropFiles->fNC = 0;
674             dropFiles->fWide = TRUE;
675             out[size] = '\0';
676             memcpy(((char*)dropFiles) + dropFiles->pFiles, out, (size + 1)*sizeof(WCHAR));
677             X11DRV_XDND_InsertXDNDData(property, CF_HDROP, dropFiles, sizeof(DROPFILES) + (size + 1)*sizeof(WCHAR));
678             count = 1;
679         }
680     }
681     HeapFree(GetProcessHeap(), 0, out);
682     return count;
683 }
684
685
686 /**************************************************************************
687  * X11DRV_XDND_DeconstructTextPlain
688  *
689  * Interpret text/plain Data and add records to <dndfmt> linked list
690  */
691 static int X11DRV_XDND_DeconstructTextPlain(int property, void* data, int len)
692 {
693     char* dostext;
694
695     /* Always supply plain text */
696     X11DRV_XDND_UnixToDos(&dostext, data, len);
697     X11DRV_XDND_InsertXDNDData(property, CF_TEXT, dostext, strlen(dostext));
698
699     TRACE("CF_TEXT (%d): %s\n", CF_TEXT, dostext);
700
701     return 1;
702 }
703
704
705 /**************************************************************************
706  * X11DRV_XDND_DeconstructTextHTML
707  *
708  * Interpret text/html data and add records to <dndfmt> linked list
709  */
710 static int X11DRV_XDND_DeconstructTextHTML(int property, void* data, int len)
711 {
712     char* dostext;
713
714     X11DRV_XDND_UnixToDos(&dostext, data, len);
715
716     X11DRV_XDND_InsertXDNDData(property,
717         RegisterClipboardFormatA("UniformResourceLocator"), dostext, strlen(dostext));
718
719     TRACE("UniformResourceLocator: %s\n", dostext);
720
721     return 1;
722 }
723
724
725 /**************************************************************************
726  * X11DRV_XDND_SendDropFiles
727  */
728 static void X11DRV_XDND_SendDropFiles(HWND hwnd)
729 {
730     LPXDNDDATA current = NULL;
731     BOOL found = FALSE;
732
733     EnterCriticalSection(&xdnd_cs);
734
735     /* Find CF_HDROP type if any */
736     LIST_FOR_EACH_ENTRY(current, &xdndData, XDNDDATA, entry)
737     {
738         if (current->cf_win == CF_HDROP)
739         {
740             found = TRUE;
741             break;
742         }
743     }
744
745     if (found)
746     {
747         DROPFILES *lpDrop = current->data;
748
749         if (lpDrop)
750         {
751             lpDrop->pt.x = XDNDxy.x;
752             lpDrop->pt.y = XDNDxy.y;
753
754             TRACE("Sending WM_DROPFILES: hWnd(0x%p) %p(%s)\n", hwnd,
755                 ((char*)lpDrop) + lpDrop->pFiles, debugstr_w((WCHAR*)(((char*)lpDrop) + lpDrop->pFiles)));
756
757             PostMessageW(hwnd, WM_DROPFILES, (WPARAM)lpDrop, 0L);
758         }
759     }
760
761     LeaveCriticalSection(&xdnd_cs);
762 }
763
764
765 /**************************************************************************
766  * X11DRV_XDND_FreeDragDropOp
767  */
768 static void X11DRV_XDND_FreeDragDropOp(void)
769 {
770     LPXDNDDATA next;
771     LPXDNDDATA current;
772
773     TRACE("\n");
774
775     EnterCriticalSection(&xdnd_cs);
776
777     /** Free data cache */
778     LIST_FOR_EACH_ENTRY_SAFE(current, next, &xdndData, XDNDDATA, entry)
779     {
780         list_remove(&current->entry);
781         HeapFree(GetProcessHeap(), 0, current);
782     }
783
784     XDNDxy.x = XDNDxy.y = 0;
785     XDNDLastTargetWnd = NULL;
786     XDNDLastDropTargetWnd = NULL;
787     XDNDAccepted = FALSE;
788
789     LeaveCriticalSection(&xdnd_cs);
790 }
791
792
793
794 /**************************************************************************
795  * X11DRV_XDND_UnixToDos
796  */
797 static unsigned int X11DRV_XDND_UnixToDos(char** lpdest, char* lpsrc, int len)
798 {
799     int i;
800     unsigned int destlen, lines;
801
802     for (i = 0, lines = 0; i <= len; i++)
803     {
804         if (lpsrc[i] == '\n')
805             lines++;
806     }
807
808     destlen = len + lines + 1;
809
810     if (lpdest)
811     {
812         char* lpstr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, destlen);
813         for (i = 0, lines = 0; i <= len; i++)
814         {
815             if (lpsrc[i] == '\n')
816                 lpstr[++lines + i] = '\r';
817             lpstr[lines + i] = lpsrc[i];
818         }
819
820         *lpdest = lpstr;
821     }
822
823     return lines;
824 }
825
826
827 /**************************************************************************
828  * X11DRV_XDND_URIToDOS
829  */
830 static WCHAR* X11DRV_XDND_URIToDOS(char *encodedURI)
831 {
832     WCHAR *ret = NULL;
833     int i;
834     int j = 0;
835     char *uri = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlen(encodedURI) + 1);
836     if (uri == NULL)
837         return NULL;
838     for (i = 0; encodedURI[i]; ++i)
839     {
840         if (encodedURI[i] == '%')
841         { 
842             if (encodedURI[i+1] && encodedURI[i+2])
843             {
844                 char buffer[3];
845                 int number;
846                 buffer[0] = encodedURI[i+1];
847                 buffer[1] = encodedURI[i+2];
848                 buffer[2] = '\0';
849                 sscanf(buffer, "%x", &number);
850                 uri[j++] = number;
851                 i += 2;
852             }
853             else
854             {
855                 WARN("invalid URI encoding in %s\n", debugstr_a(encodedURI));
856                 HeapFree(GetProcessHeap(), 0, uri);
857                 return NULL;
858             }
859         }
860         else
861             uri[j++] = encodedURI[i];
862     }
863
864     /* Read http://www.freedesktop.org/wiki/Draganddropwarts and cry... */
865     if (strncmp(uri, "file:/", 6) == 0)
866     {
867         if (uri[6] == '/')
868         {
869             if (uri[7] == '/')
870             {
871                 /* file:///path/to/file (nautilus, thunar) */
872                 ret = wine_get_dos_file_name(&uri[7]);
873             }
874             else if (uri[7])
875             {
876                 /* file://hostname/path/to/file (X file drag spec) */
877                 char hostname[256];
878                 char *path = strchr(&uri[7], '/');
879                 if (path)
880                 {
881                     *path = '\0';
882                     if (strcmp(&uri[7], "localhost") == 0)
883                     {
884                         *path = '/';
885                         ret = wine_get_dos_file_name(path);
886                     }
887                     else if (gethostname(hostname, sizeof(hostname)) == 0)
888                     {
889                         if (strcmp(hostname, &uri[7]) == 0)
890                         {
891                             *path = '/';
892                             ret = wine_get_dos_file_name(path);
893                         }
894                     }
895                 }
896             }
897         }
898         else if (uri[6])
899         {
900             /* file:/path/to/file (konqueror) */
901             ret = wine_get_dos_file_name(&uri[5]);
902         }
903     }
904     HeapFree(GetProcessHeap(), 0, uri);
905     return ret;
906 }
907
908
909 /**************************************************************************
910  * X11DRV_XDND_DescribeClipboardFormat
911  */
912 static void X11DRV_XDND_DescribeClipboardFormat(int cfFormat, char *buffer, int size)
913 {
914 #define D(x) case x: lstrcpynA(buffer, #x, size); return;
915     switch (cfFormat)
916     {
917         D(CF_TEXT)
918         D(CF_BITMAP)
919         D(CF_METAFILEPICT)
920         D(CF_SYLK)
921         D(CF_DIF)
922         D(CF_TIFF)
923         D(CF_OEMTEXT)
924         D(CF_DIB)
925         D(CF_PALETTE)
926         D(CF_PENDATA)
927         D(CF_RIFF)
928         D(CF_WAVE)
929         D(CF_UNICODETEXT)
930         D(CF_ENHMETAFILE)
931         D(CF_HDROP)
932         D(CF_LOCALE)
933         D(CF_DIBV5)
934     }
935 #undef D
936
937     if (CF_PRIVATEFIRST <= cfFormat && cfFormat <= CF_PRIVATELAST)
938     {
939         lstrcpynA(buffer, "some private object", size);
940         return;
941     }
942     if (CF_GDIOBJFIRST <= cfFormat && cfFormat <= CF_GDIOBJLAST)
943     {
944         lstrcpynA(buffer, "some GDI object", size);
945         return;
946     }
947
948     GetClipboardFormatNameA(cfFormat, buffer, size);
949 }
950
951
952 /* The IDataObject singleton we feed to OLE follows */
953
954 static HRESULT WINAPI XDNDDATAOBJECT_QueryInterface(IDataObject *dataObject,
955                                                     REFIID riid, void **ppvObject)
956 {
957     TRACE("(%p, %s, %p)\n", dataObject, debugstr_guid(riid), ppvObject);
958     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDataObject))
959     {
960         *ppvObject = dataObject;
961         IDataObject_AddRef(dataObject);
962         return S_OK;
963     }
964     *ppvObject = NULL;
965     return E_NOINTERFACE;
966 }
967
968 static ULONG WINAPI XDNDDATAOBJECT_AddRef(IDataObject *dataObject)
969 {
970     TRACE("(%p)\n", dataObject);
971     return 2;
972 }
973
974 static ULONG WINAPI XDNDDATAOBJECT_Release(IDataObject *dataObject)
975 {
976     TRACE("(%p)\n", dataObject);
977     return 1;
978 }
979
980 static HRESULT WINAPI XDNDDATAOBJECT_GetData(IDataObject *dataObject,
981                                              FORMATETC *formatEtc,
982                                              STGMEDIUM *pMedium)
983 {
984     HRESULT hr;
985     char formatDesc[1024];
986
987     TRACE("(%p, %p, %p)\n", dataObject, formatEtc, pMedium);
988     X11DRV_XDND_DescribeClipboardFormat(formatEtc->cfFormat,
989         formatDesc, sizeof(formatDesc));
990     TRACE("application is looking for %s\n", formatDesc);
991
992     hr = IDataObject_QueryGetData(dataObject, formatEtc);
993     if (SUCCEEDED(hr))
994     {
995         XDNDDATA *current;
996         LIST_FOR_EACH_ENTRY(current, &xdndData, XDNDDATA, entry)
997         {
998             if (current->cf_win == formatEtc->cfFormat)
999             {
1000                 pMedium->tymed = TYMED_HGLOBAL;
1001                 pMedium->u.hGlobal = HeapAlloc(GetProcessHeap(), 0, current->size);
1002                 if (pMedium->u.hGlobal == NULL)
1003                     return E_OUTOFMEMORY;
1004                 memcpy(pMedium->u.hGlobal, current->data, current->size);
1005                 pMedium->pUnkForRelease = 0;
1006                 return S_OK;
1007             }
1008         }
1009     }
1010     return hr;
1011 }
1012
1013 static HRESULT WINAPI XDNDDATAOBJECT_GetDataHere(IDataObject *dataObject,
1014                                                  FORMATETC *formatEtc,
1015                                                  STGMEDIUM *pMedium)
1016 {
1017     FIXME("(%p, %p, %p): stub\n", dataObject, formatEtc, pMedium);
1018     return DATA_E_FORMATETC;
1019 }
1020
1021 static HRESULT WINAPI XDNDDATAOBJECT_QueryGetData(IDataObject *dataObject,
1022                                                   FORMATETC *formatEtc)
1023 {
1024     char formatDesc[1024];
1025     XDNDDATA *current;
1026
1027     TRACE("(%p, %p={.tymed=0x%x, .dwAspect=%d, .cfFormat=%d}\n",
1028         dataObject, formatEtc, formatEtc->tymed, formatEtc->dwAspect, formatEtc->cfFormat);
1029     X11DRV_XDND_DescribeClipboardFormat(formatEtc->cfFormat, formatDesc, sizeof(formatDesc));
1030
1031     if (formatEtc->tymed && !(formatEtc->tymed & TYMED_HGLOBAL))
1032     {
1033         FIXME("only HGLOBAL medium types supported right now\n");
1034         return DV_E_TYMED;
1035     }
1036     if (formatEtc->dwAspect != DVASPECT_CONTENT)
1037     {
1038         FIXME("only the content aspect is supported right now\n");
1039         return E_NOTIMPL;
1040     }
1041
1042     LIST_FOR_EACH_ENTRY(current, &xdndData, XDNDDATA, entry)
1043     {
1044         if (current->cf_win == formatEtc->cfFormat)
1045         {
1046             TRACE("application found %s\n", formatDesc);
1047             return S_OK;
1048         }
1049     }
1050     TRACE("application didn't find %s\n", formatDesc);
1051     return DV_E_FORMATETC;
1052 }
1053
1054 static HRESULT WINAPI XDNDDATAOBJECT_GetCanonicalFormatEtc(IDataObject *dataObject,
1055                                                            FORMATETC *formatEtc,
1056                                                            FORMATETC *formatEtcOut)
1057 {
1058     FIXME("(%p, %p, %p): stub\n", dataObject, formatEtc, formatEtcOut);
1059     formatEtcOut->ptd = NULL;
1060     return E_NOTIMPL;
1061 }
1062
1063 static HRESULT WINAPI XDNDDATAOBJECT_SetData(IDataObject *dataObject,
1064                                              FORMATETC *formatEtc,
1065                                              STGMEDIUM *pMedium, BOOL fRelease)
1066 {
1067     FIXME("(%p, %p, %p, %s): stub\n", dataObject, formatEtc,
1068         pMedium, fRelease?"TRUE":"FALSE");
1069     return E_NOTIMPL;
1070 }
1071
1072 static HRESULT WINAPI XDNDDATAOBJECT_EnumFormatEtc(IDataObject *dataObject,
1073                                                    DWORD dwDirection,
1074                                                    IEnumFORMATETC **ppEnumFormatEtc)
1075 {
1076     DWORD count;
1077     FORMATETC *formats;
1078
1079     TRACE("(%p, %u, %p)\n", dataObject, dwDirection, ppEnumFormatEtc);
1080
1081     if (dwDirection != DATADIR_GET)
1082     {
1083         FIXME("only the get direction is implemented\n");
1084         return E_NOTIMPL;
1085     }
1086
1087     count = list_count(&xdndData);
1088     formats = HeapAlloc(GetProcessHeap(), 0, count * sizeof(FORMATETC));
1089     if (formats)
1090     {
1091         XDNDDATA *current;
1092         DWORD i = 0;
1093         HRESULT hr;
1094         LIST_FOR_EACH_ENTRY(current, &xdndData, XDNDDATA, entry)
1095         {
1096             formats[i].cfFormat = current->cf_win;
1097             formats[i].ptd = NULL;
1098             formats[i].dwAspect = DVASPECT_CONTENT;
1099             formats[i].lindex = -1;
1100             formats[i].tymed = TYMED_HGLOBAL;
1101             i++;
1102         }
1103         hr = SHCreateStdEnumFmtEtc(count, formats, ppEnumFormatEtc);
1104         HeapFree(GetProcessHeap(), 0, formats);
1105         return hr;
1106     }
1107     else
1108         return E_OUTOFMEMORY;
1109 }
1110
1111 static HRESULT WINAPI XDNDDATAOBJECT_DAdvise(IDataObject *dataObject,
1112                                              FORMATETC *formatEtc, DWORD advf,
1113                                              IAdviseSink *adviseSink,
1114                                              DWORD *pdwConnection)
1115 {
1116     FIXME("(%p, %p, %u, %p, %p): stub\n", dataObject, formatEtc, advf,
1117         adviseSink, pdwConnection);
1118     return OLE_E_ADVISENOTSUPPORTED;
1119 }
1120
1121 static HRESULT WINAPI XDNDDATAOBJECT_DUnadvise(IDataObject *dataObject,
1122                                                DWORD dwConnection)
1123 {
1124     FIXME("(%p, %u): stub\n", dataObject, dwConnection);
1125     return OLE_E_ADVISENOTSUPPORTED;
1126 }
1127
1128 static HRESULT WINAPI XDNDDATAOBJECT_EnumDAdvise(IDataObject *dataObject,
1129                                                  IEnumSTATDATA **pEnumAdvise)
1130 {
1131     FIXME("(%p, %p): stub\n", dataObject, pEnumAdvise);
1132     return OLE_E_ADVISENOTSUPPORTED;
1133 }
1134
1135 static IDataObjectVtbl xdndDataObjectVtbl =
1136 {
1137     XDNDDATAOBJECT_QueryInterface,
1138     XDNDDATAOBJECT_AddRef,
1139     XDNDDATAOBJECT_Release,
1140     XDNDDATAOBJECT_GetData,
1141     XDNDDATAOBJECT_GetDataHere,
1142     XDNDDATAOBJECT_QueryGetData,
1143     XDNDDATAOBJECT_GetCanonicalFormatEtc,
1144     XDNDDATAOBJECT_SetData,
1145     XDNDDATAOBJECT_EnumFormatEtc,
1146     XDNDDATAOBJECT_DAdvise,
1147     XDNDDATAOBJECT_DUnadvise,
1148     XDNDDATAOBJECT_EnumDAdvise
1149 };
1150
1151 static IDataObject XDNDDataObject = { &xdndDataObjectVtbl };