netstat: Initial implementation.
[wine] / dlls / shell32 / recyclebin.c
1 /*
2  * Trash virtual folder support. The trashing engine is implemented in trash.c
3  *
4  * Copyright (C) 2006 Mikolaj Zalewski
5  * Copyright 2011 Jay Yang
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
24 #define COBJMACROS
25 #define NONAMELESSUNION
26
27 #include <stdarg.h>
28
29 #include "winerror.h"
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winreg.h"
33 #include "winuser.h"
34 #include "shlwapi.h"
35 #include "ntquery.h"
36 #include "shlobj.h"
37 #include "shresdef.h"
38 #include "shellfolder.h"
39 #include "shellapi.h"
40 #include "knownfolders.h"
41 #include "wine/debug.h"
42
43 #include "shell32_main.h"
44 #include "xdg.h"
45 #include "pidl.h"
46
47 WINE_DEFAULT_DEBUG_CHANNEL(recyclebin);
48
49 typedef struct
50 {
51     int column_name_id;
52     const GUID *fmtId;
53     DWORD pid;
54     int pcsFlags;
55     int fmt;
56     int cxChars;
57 } columninfo;
58
59 static const columninfo RecycleBinColumns[] =
60 {
61     {IDS_SHV_COLUMN1,        &FMTID_Storage,   PID_STG_NAME,       SHCOLSTATE_TYPE_STR|SHCOLSTATE_ONBYDEFAULT,  LVCFMT_LEFT,  30},
62     {IDS_SHV_COLUMN_DELFROM, &FMTID_Displaced, PID_DISPLACED_FROM, SHCOLSTATE_TYPE_STR|SHCOLSTATE_ONBYDEFAULT,  LVCFMT_LEFT,  30},
63     {IDS_SHV_COLUMN_DELDATE, &FMTID_Displaced, PID_DISPLACED_DATE, SHCOLSTATE_TYPE_DATE|SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT,  20},
64     {IDS_SHV_COLUMN2,        &FMTID_Storage,   PID_STG_SIZE,       SHCOLSTATE_TYPE_INT|SHCOLSTATE_ONBYDEFAULT,  LVCFMT_RIGHT, 20},
65     {IDS_SHV_COLUMN3,        &FMTID_Storage,   PID_STG_STORAGETYPE,SHCOLSTATE_TYPE_INT|SHCOLSTATE_ONBYDEFAULT,  LVCFMT_LEFT,  20},
66     {IDS_SHV_COLUMN4,        &FMTID_Storage,   PID_STG_WRITETIME,  SHCOLSTATE_TYPE_DATE|SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT,  20},
67 /*    {"creation time",  &FMTID_Storage,   PID_STG_CREATETIME, SHCOLSTATE_TYPE_DATE,                        LVCFMT_LEFT,  20}, */
68 /*    {"attribs",        &FMTID_Storage,   PID_STG_ATTRIBUTES, SHCOLSTATE_TYPE_STR,                         LVCFMT_LEFT,  20},       */
69 };
70
71 #define COLUMN_NAME    0
72 #define COLUMN_DELFROM 1
73 #define COLUMN_DATEDEL 2
74 #define COLUMN_SIZE    3
75 #define COLUMN_TYPE    4
76 #define COLUMN_MTIME   5
77
78 #define COLUMNS_COUNT  6
79
80 static HRESULT FormatDateTime(LPWSTR buffer, int size, FILETIME ft)
81 {
82     FILETIME lft;
83     SYSTEMTIME time;
84     int ret;
85
86     FileTimeToLocalFileTime(&ft, &lft);
87     FileTimeToSystemTime(&lft, &time);
88
89     ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, buffer, size);
90     if (ret>0 && ret<size)
91     {
92         /* Append space + time without seconds */
93         buffer[ret-1] = ' ';
94         GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL, &buffer[ret], size - ret);
95     }
96
97     return (ret!=0 ? E_FAIL : S_OK);
98 }
99
100 typedef struct tagRecycleBinMenu
101 {
102     IContextMenu2 IContextMenu2_iface;
103     LONG refCount;
104
105     UINT cidl;
106     LPITEMIDLIST *apidl;
107     IShellFolder2 *folder;
108 } RecycleBinMenu;
109
110 static const IContextMenu2Vtbl recycleBinMenuVtbl;
111
112 static RecycleBinMenu *impl_from_IContextMenu2(IContextMenu2 *iface)
113 {
114     return CONTAINING_RECORD(iface, RecycleBinMenu, IContextMenu2_iface);
115 }
116
117 static IContextMenu2* RecycleBinMenu_Constructor(UINT cidl, LPCITEMIDLIST *apidl, IShellFolder2 *folder)
118 {
119     RecycleBinMenu *This = SHAlloc(sizeof(RecycleBinMenu));
120     TRACE("(%u,%p)\n",cidl,apidl);
121     This->IContextMenu2_iface.lpVtbl = &recycleBinMenuVtbl;
122     This->cidl = cidl;
123     This->apidl = _ILCopyaPidl(apidl,cidl);
124     IShellFolder2_AddRef(folder);
125     This->folder = folder;
126     This->refCount = 1;
127     return &This->IContextMenu2_iface;
128 }
129
130 static HRESULT WINAPI RecycleBinMenu_QueryInterface(IContextMenu2 *iface,
131                                                     REFIID riid,
132                                                     void **ppvObject)
133 {
134     RecycleBinMenu *This = impl_from_IContextMenu2(iface);
135     TRACE("(%p, %s, %p) - stub\n", This, debugstr_guid(riid), ppvObject);
136     return E_NOTIMPL;
137 }
138
139 static ULONG WINAPI RecycleBinMenu_AddRef(IContextMenu2 *iface)
140 {
141     RecycleBinMenu *This = impl_from_IContextMenu2(iface);
142     TRACE("(%p)\n", This);
143     return InterlockedIncrement(&This->refCount);
144
145 }
146
147 static ULONG WINAPI RecycleBinMenu_Release(IContextMenu2 *iface)
148 {
149     RecycleBinMenu *This = impl_from_IContextMenu2(iface);
150     UINT result;
151     TRACE("(%p)\n", This);
152     result = InterlockedDecrement(&This->refCount);
153     if (result == 0)
154     {
155         TRACE("Destroying object\n");
156         _ILFreeaPidl(This->apidl,This->cidl);
157         IShellFolder2_Release(This->folder);
158         SHFree(This);
159     }
160     return result;
161 }
162
163 static HRESULT WINAPI RecycleBinMenu_QueryContextMenu(IContextMenu2 *iface,
164                                                       HMENU hmenu,
165                                                       UINT indexMenu,
166                                                       UINT idCmdFirst,
167                                                       UINT idCmdLast,
168                                                       UINT uFlags)
169 {
170     HMENU menures = LoadMenuW(shell32_hInstance,MAKEINTRESOURCEW(MENU_RECYCLEBIN));
171     if(uFlags & CMF_DEFAULTONLY)
172         return E_NOTIMPL;
173     else{
174         UINT idMax = Shell_MergeMenus(hmenu,GetSubMenu(menures,0),indexMenu,idCmdFirst,idCmdLast,MM_SUBMENUSHAVEIDS);
175         TRACE("Added %d id(s)\n",idMax-idCmdFirst);
176         return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, idMax-idCmdFirst+1);
177     }
178 }
179
180 static void DoErase(RecycleBinMenu *This)
181 {
182     ISFHelper *helper;
183     IShellFolder2_QueryInterface(This->folder,&IID_ISFHelper,(void**)&helper);
184     if(helper)
185         ISFHelper_DeleteItems(helper,This->cidl,(LPCITEMIDLIST*)This->apidl);
186 }
187
188 static void DoRestore(RecycleBinMenu *This)
189 {
190
191     /*TODO add prompts*/
192     UINT i;
193     for(i=0;i<This->cidl;i++)
194     {
195         WIN32_FIND_DATAW data;
196         TRASH_UnpackItemID(&((This->apidl[i])->mkid),&data);
197         if(PathFileExistsW(data.cFileName))
198         {
199             PIDLIST_ABSOLUTE dest_pidl = ILCreateFromPathW(data.cFileName);
200             WCHAR message[100];
201             WCHAR caption[50];
202             if(_ILIsFolder(ILFindLastID(dest_pidl)))
203                 LoadStringW(shell32_hInstance,IDS_RECYCLEBIN_OVERWRITEFOLDER,
204                             message,sizeof(message)/sizeof(WCHAR));
205             else
206                 LoadStringW(shell32_hInstance,IDS_RECYCLEBIN_OVERWRITEFILE,
207                             message,sizeof(message)/sizeof(WCHAR));
208             LoadStringW(shell32_hInstance,IDS_RECYCLEBIN_OVERWRITE_CAPTION,
209                         caption,sizeof(caption)/sizeof(WCHAR));
210
211             if(ShellMessageBoxW(shell32_hInstance,GetActiveWindow(),message,
212                                 caption,MB_YESNO|MB_ICONEXCLAMATION,
213                                 data.cFileName)!=IDYES)
214                 continue;
215         }
216         if(SUCCEEDED(TRASH_RestoreItem(This->apidl[i])))
217         {
218             IPersistFolder2 *persist;
219             LPITEMIDLIST root_pidl;
220             PIDLIST_ABSOLUTE dest_pidl = ILCreateFromPathW(data.cFileName);
221             BOOL is_folder = _ILIsFolder(ILFindLastID(dest_pidl));
222             IShellFolder2_QueryInterface(This->folder,&IID_IPersistFolder2,
223                                          (void**)&persist);
224             IPersistFolder2_GetCurFolder(persist,&root_pidl);
225             SHChangeNotify(is_folder ? SHCNE_RMDIR : SHCNE_DELETE,
226                            SHCNF_IDLIST,ILCombine(root_pidl,This->apidl[i]),0);
227             SHChangeNotify(is_folder ? SHCNE_MKDIR : SHCNE_CREATE,
228                            SHCNF_IDLIST,dest_pidl,0);
229             ILFree(dest_pidl);
230             ILFree(root_pidl);
231         }
232     }
233 }
234
235 static HRESULT WINAPI RecycleBinMenu_InvokeCommand(IContextMenu2 *iface,
236                                                    LPCMINVOKECOMMANDINFO pici)
237 {
238     RecycleBinMenu *This = impl_from_IContextMenu2(iface);
239     LPCSTR verb = pici->lpVerb;
240     if(IS_INTRESOURCE(verb))
241     {
242         switch(LOWORD(verb))
243         {
244         case IDM_RECYCLEBIN_ERASE:
245             DoErase(This);
246             break;
247         case IDM_RECYCLEBIN_RESTORE:
248             DoRestore(This);
249             break;
250         default:
251             return E_NOTIMPL;
252         }
253     }
254     return S_OK;
255 }
256
257 static HRESULT WINAPI RecycleBinMenu_GetCommandString(IContextMenu2 *iface,
258                                                       UINT_PTR idCmd,
259                                                       UINT uType,
260                                                       UINT *pwReserved,
261                                                       LPSTR pszName,
262                                                       UINT cchMax)
263 {
264     TRACE("(%p, %lu, %u, %p, %s, %u) - stub\n",iface,idCmd,uType,pwReserved,debugstr_a(pszName),cchMax);
265     return E_NOTIMPL;
266 }
267
268 static HRESULT WINAPI RecycleBinMenu_HandleMenuMsg(IContextMenu2 *iface,
269                                                    UINT uMsg, WPARAM wParam,
270                                                    LPARAM lParam)
271 {
272     TRACE("(%p, %u, 0x%lx, 0x%lx) - stub\n",iface,uMsg,wParam,lParam);
273     return E_NOTIMPL;
274 }
275
276
277 static const IContextMenu2Vtbl recycleBinMenuVtbl =
278 {
279     RecycleBinMenu_QueryInterface,
280     RecycleBinMenu_AddRef,
281     RecycleBinMenu_Release,
282     RecycleBinMenu_QueryContextMenu,
283     RecycleBinMenu_InvokeCommand,
284     RecycleBinMenu_GetCommandString,
285     RecycleBinMenu_HandleMenuMsg,
286 };
287
288 /*
289  * Recycle Bin folder
290  */
291
292 typedef struct tagRecycleBin
293 {
294     IShellFolder2 IShellFolder2_iface;
295     IPersistFolder2 IPersistFolder2_iface;
296     ISFHelper ISFHelper_iface;
297     LONG refCount;
298
299     LPITEMIDLIST pidl;
300 } RecycleBin;
301
302 static const IShellFolder2Vtbl recycleBinVtbl;
303 static const IPersistFolder2Vtbl recycleBinPersistVtbl;
304 static const ISFHelperVtbl sfhelperVtbl;
305
306 static inline RecycleBin *impl_from_IShellFolder2(IShellFolder2 *iface)
307 {
308     return CONTAINING_RECORD(iface, RecycleBin, IShellFolder2_iface);
309 }
310
311 static RecycleBin *impl_from_IPersistFolder2(IPersistFolder2 *iface)
312 {
313     return CONTAINING_RECORD(iface, RecycleBin, IPersistFolder2_iface);
314 }
315
316 static RecycleBin *impl_from_ISFHelper(ISFHelper *iface)
317 {
318     return CONTAINING_RECORD(iface, RecycleBin, ISFHelper_iface);
319 }
320
321 static void RecycleBin_Destructor(RecycleBin *This);
322
323 HRESULT WINAPI RecycleBin_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppOutput)
324 {
325     RecycleBin *obj;
326     HRESULT ret;
327     if (pUnkOuter)
328         return CLASS_E_NOAGGREGATION;
329
330     obj = SHAlloc(sizeof(RecycleBin));
331     if (obj == NULL)
332         return E_OUTOFMEMORY;
333     ZeroMemory(obj, sizeof(RecycleBin));
334     obj->IShellFolder2_iface.lpVtbl = &recycleBinVtbl;
335     obj->IPersistFolder2_iface.lpVtbl = &recycleBinPersistVtbl;
336     obj->ISFHelper_iface.lpVtbl = &sfhelperVtbl;
337     if (FAILED(ret = IPersistFolder2_QueryInterface(&obj->IPersistFolder2_iface, riid, ppOutput)))
338     {
339         RecycleBin_Destructor(obj);
340         return ret;
341     }
342 /*    InterlockedIncrement(&objCount);*/
343     return S_OK;
344 }
345
346 static void RecycleBin_Destructor(RecycleBin *This)
347 {
348 /*    InterlockedDecrement(&objCount);*/
349     SHFree(This->pidl);
350     SHFree(This);
351 }
352
353 static HRESULT WINAPI RecycleBin_QueryInterface(IShellFolder2 *iface, REFIID riid, void **ppvObject)
354 {
355     RecycleBin *This = impl_from_IShellFolder2(iface);
356     TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
357
358     *ppvObject = NULL;
359     if (IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IShellFolder)
360             || IsEqualGUID(riid, &IID_IShellFolder2))
361         *ppvObject = This;
362
363     if (IsEqualGUID(riid, &IID_IPersist) || IsEqualGUID(riid, &IID_IPersistFolder)
364             || IsEqualGUID(riid, &IID_IPersistFolder2))
365         *ppvObject = &This->IPersistFolder2_iface;
366     if (IsEqualGUID(riid, &IID_ISFHelper))
367         *ppvObject = &This->ISFHelper_iface;
368
369     if (*ppvObject != NULL)
370     {
371         IUnknown_AddRef((IUnknown *)*ppvObject);
372         return S_OK;
373     }
374     WARN("no interface %s\n", debugstr_guid(riid));
375     return E_NOINTERFACE;
376 }
377
378 static ULONG WINAPI RecycleBin_AddRef(IShellFolder2 *iface)
379 {
380     RecycleBin *This = impl_from_IShellFolder2(iface);
381     TRACE("(%p)\n", This);
382     return InterlockedIncrement(&This->refCount);
383 }
384
385 static ULONG WINAPI RecycleBin_Release(IShellFolder2 *iface)
386 {
387     RecycleBin *This = impl_from_IShellFolder2(iface);
388     LONG result;
389
390     TRACE("(%p)\n", This);
391     result = InterlockedDecrement(&This->refCount);
392     if (result == 0)
393     {
394         TRACE("Destroy object\n");
395         RecycleBin_Destructor(This);
396     }
397     return result;
398 }
399
400 static HRESULT WINAPI RecycleBin_ParseDisplayName(IShellFolder2 *This, HWND hwnd, LPBC pbc,
401             LPOLESTR pszDisplayName, ULONG *pchEaten, LPITEMIDLIST *ppidl,
402             ULONG *pdwAttributes)
403 {
404     FIXME("stub\n");
405     return E_NOTIMPL;
406 }
407
408 static HRESULT WINAPI RecycleBin_EnumObjects(IShellFolder2 *iface, HWND hwnd, SHCONTF grfFlags, IEnumIDList **ppenumIDList)
409 {
410     RecycleBin *This = impl_from_IShellFolder2(iface);
411     IEnumIDListImpl *list;
412     LPITEMIDLIST *pidls;
413     HRESULT ret = E_OUTOFMEMORY;
414     int pidls_count;
415     int i=0;
416
417     TRACE("(%p, %p, %x, %p)\n", This, hwnd, grfFlags, ppenumIDList);
418
419     *ppenumIDList = NULL;
420     list = IEnumIDList_Constructor();
421     if (!list)
422         return E_OUTOFMEMORY;
423
424     if (grfFlags & SHCONTF_NONFOLDERS)
425     {
426         if (FAILED(ret = TRASH_EnumItems(&pidls, &pidls_count)))
427             goto failed;
428         for (i=0; i<pidls_count; i++)
429             if (!AddToEnumList(list, pidls[i]))
430                 goto failed;
431     }
432
433     *ppenumIDList = &list->IEnumIDList_iface;
434     return S_OK;
435
436 failed:
437     if (list)
438         IEnumIDList_Release(&list->IEnumIDList_iface);
439     for (; i<pidls_count; i++)
440         ILFree(pidls[i]);
441     SHFree(pidls);
442     return ret;
443 }
444
445 static HRESULT WINAPI RecycleBin_BindToObject(IShellFolder2 *This, LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
446 {
447     FIXME("(%p, %p, %p, %s, %p) - stub\n", This, pidl, pbc, debugstr_guid(riid), ppv);
448     return E_NOTIMPL;
449 }
450
451 static HRESULT WINAPI RecycleBin_BindToStorage(IShellFolder2 *This, LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
452 {
453     FIXME("(%p, %p, %p, %s, %p) - stub\n", This, pidl, pbc, debugstr_guid(riid), ppv);
454     return E_NOTIMPL;
455 }
456
457 static HRESULT WINAPI RecycleBin_CompareIDs(IShellFolder2 *iface, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
458 {
459     RecycleBin *This = impl_from_IShellFolder2(iface);
460     int ret;
461
462     /* TODO */
463     TRACE("(%p, %p, %p, %p)\n", This, (void *)lParam, pidl1, pidl2);
464     if (pidl1->mkid.cb != pidl2->mkid.cb)
465         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, pidl1->mkid.cb - pidl2->mkid.cb);
466     /* Looks too complicated, but in optimized memcpy we might get
467      * a 32bit wide difference and would truncate it to 16 bit, so
468      * erroneously returning equality. */
469     ret = memcmp(pidl1->mkid.abID, pidl2->mkid.abID, pidl1->mkid.cb);
470     if (ret < 0) ret = -1;
471     if (ret > 0) ret =  1;
472     return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (unsigned short)ret);
473 }
474
475 static HRESULT WINAPI RecycleBin_CreateViewObject(IShellFolder2 *iface, HWND hwndOwner, REFIID riid, void **ppv)
476 {
477     RecycleBin *This = impl_from_IShellFolder2(iface);
478     HRESULT ret;
479     TRACE("(%p, %p, %s, %p)\n", This, hwndOwner, debugstr_guid(riid), ppv);
480
481     *ppv = NULL;
482     if (IsEqualGUID(riid, &IID_IShellView))
483     {
484         IShellView *tmp;
485         CSFV sfv;
486
487         ZeroMemory(&sfv, sizeof(sfv));
488         sfv.cbSize = sizeof(sfv);
489         sfv.pshf = (IShellFolder *)This;
490
491         TRACE("Calling SHCreateShellFolderViewEx\n");
492         ret = SHCreateShellFolderViewEx(&sfv, &tmp);
493         TRACE("Result: %08x, output: %p\n", (unsigned int)ret, tmp);
494         *ppv = tmp;
495         return ret;
496     }
497
498     return E_NOINTERFACE;
499 }
500
501 static HRESULT WINAPI RecycleBin_GetAttributesOf(IShellFolder2 *This, UINT cidl, LPCITEMIDLIST *apidl,
502                                    SFGAOF *rgfInOut)
503 {
504     TRACE("(%p, %d, {%p, ...}, {%x})\n", This, cidl, apidl[0], *rgfInOut);
505     *rgfInOut &= SFGAO_CANMOVE|SFGAO_CANDELETE|SFGAO_HASPROPSHEET|SFGAO_FILESYSTEM;
506     return S_OK;
507 }
508
509 static HRESULT WINAPI RecycleBin_GetUIObjectOf(IShellFolder2 *iface, HWND hwndOwner, UINT cidl, LPCITEMIDLIST *apidl,
510                       REFIID riid, UINT *rgfReserved, void **ppv)
511 {
512     RecycleBin *This = impl_from_IShellFolder2(iface);
513     *ppv = NULL;
514     if(IsEqualGUID(riid, &IID_IContextMenu) || IsEqualGUID(riid, &IID_IContextMenu2))
515     {
516         TRACE("(%p, %p, %d, {%p, ...}, %s, %p, %p)\n", This, hwndOwner, cidl, apidl[0], debugstr_guid(riid), rgfReserved, ppv);
517         *ppv = RecycleBinMenu_Constructor(cidl,apidl,&(This->IShellFolder2_iface));
518         return S_OK;
519     }
520     FIXME("(%p, %p, %d, {%p, ...}, %s, %p, %p): stub!\n", iface, hwndOwner, cidl, apidl[0], debugstr_guid(riid), rgfReserved, ppv);
521
522     return E_NOTIMPL;
523 }
524
525 static HRESULT WINAPI RecycleBin_GetDisplayNameOf(IShellFolder2 *This, LPCITEMIDLIST pidl, SHGDNF uFlags, STRRET *pName)
526 {
527     WIN32_FIND_DATAW data;
528
529     TRACE("(%p, %p, %x, %p)\n", This, pidl, uFlags, pName);
530     TRASH_UnpackItemID(&pidl->mkid, &data);
531     pName->uType = STRRET_WSTR;
532     pName->u.pOleStr = StrDupW(PathFindFileNameW(data.cFileName));
533     if (pName->u.pOleStr == NULL)
534         return E_OUTOFMEMORY;
535
536     return S_OK;
537 }
538
539 static HRESULT WINAPI RecycleBin_SetNameOf(IShellFolder2 *This, HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName,
540             SHGDNF uFlags, LPITEMIDLIST *ppidlOut)
541 {
542     TRACE("\n");
543     return E_FAIL; /* not supported */
544 }
545
546 static HRESULT WINAPI RecycleBin_GetClassID(IPersistFolder2 *This, CLSID *pClassID)
547 {
548     TRACE("(%p, %p)\n", This, pClassID);
549     if (This == NULL || pClassID == NULL)
550         return E_INVALIDARG;
551     *pClassID = CLSID_RecycleBin;
552     return S_OK;
553 }
554
555 static HRESULT WINAPI RecycleBin_Initialize(IPersistFolder2 *iface, LPCITEMIDLIST pidl)
556 {
557     RecycleBin *This = impl_from_IPersistFolder2(iface);
558     TRACE("(%p, %p)\n", This, pidl);
559
560     This->pidl = ILClone(pidl);
561     if (This->pidl == NULL)
562         return E_OUTOFMEMORY;
563     return S_OK;
564 }
565
566 static HRESULT WINAPI RecycleBin_GetCurFolder(IPersistFolder2 *iface, LPITEMIDLIST *ppidl)
567 {
568     RecycleBin *This = impl_from_IPersistFolder2(iface);
569     TRACE("\n");
570     *ppidl = ILClone(This->pidl);
571     return S_OK;
572 }
573
574 static HRESULT WINAPI RecycleBin_GetDefaultSearchGUID(IShellFolder2 *iface, GUID *pguid)
575 {
576     FIXME("stub\n");
577     return E_NOTIMPL;
578 }
579
580 static HRESULT WINAPI RecycleBin_EnumSearches(IShellFolder2 *iface, IEnumExtraSearch **ppEnum)
581 {
582     FIXME("stub\n");
583     *ppEnum = NULL;
584     return E_NOTIMPL;
585 }
586
587 static HRESULT WINAPI RecycleBin_GetDefaultColumn(IShellFolder2 *iface, DWORD dwReserved, ULONG *pSort, ULONG *pDisplay)
588 {
589     RecycleBin *This = impl_from_IShellFolder2(iface);
590     TRACE("(%p, %x, %p, %p)\n", This, dwReserved, pSort, pDisplay);
591     *pSort = 0;
592     *pDisplay = 0;
593     return S_OK;
594 }
595
596 static HRESULT WINAPI RecycleBin_GetDefaultColumnState(IShellFolder2 *iface, UINT iColumn, SHCOLSTATEF *pcsFlags)
597 {
598     RecycleBin *This = impl_from_IShellFolder2(iface);
599     TRACE("(%p, %d, %p)\n", This, iColumn, pcsFlags);
600     if (iColumn >= COLUMNS_COUNT)
601         return E_INVALIDARG;
602     *pcsFlags = RecycleBinColumns[iColumn].pcsFlags;
603     return S_OK;
604 }
605
606 static HRESULT WINAPI RecycleBin_GetDetailsEx(IShellFolder2 *iface, LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
607 {
608     FIXME("stub\n");
609     return E_NOTIMPL;
610 }
611
612 static HRESULT WINAPI RecycleBin_GetDetailsOf(IShellFolder2 *iface, LPCITEMIDLIST pidl, UINT iColumn, LPSHELLDETAILS pDetails)
613 {
614     RecycleBin *This = impl_from_IShellFolder2(iface);
615     WIN32_FIND_DATAW data;
616     WCHAR buffer[MAX_PATH];
617
618     TRACE("(%p, %p, %d, %p)\n", This, pidl, iColumn, pDetails);
619     if (iColumn >= COLUMNS_COUNT)
620         return E_FAIL;
621     pDetails->fmt = RecycleBinColumns[iColumn].fmt;
622     pDetails->cxChar = RecycleBinColumns[iColumn].cxChars;
623     if (pidl == NULL)
624     {
625         pDetails->str.uType = STRRET_WSTR;
626         LoadStringW(shell32_hInstance, RecycleBinColumns[iColumn].column_name_id, buffer, MAX_PATH);
627         return SHStrDupW(buffer, &pDetails->str.u.pOleStr);
628     }
629
630     if (iColumn == COLUMN_NAME)
631         return RecycleBin_GetDisplayNameOf(iface, pidl, SHGDN_NORMAL, &pDetails->str);
632
633     TRASH_UnpackItemID(&pidl->mkid, &data);
634     switch (iColumn)
635     {
636         case COLUMN_DATEDEL:
637             FormatDateTime(buffer, MAX_PATH, data.ftLastAccessTime);
638             break;
639         case COLUMN_DELFROM:
640             lstrcpyW(buffer, data.cFileName);
641             PathRemoveFileSpecW(buffer);
642             break;
643         case COLUMN_SIZE:
644             StrFormatKBSizeW(((LONGLONG)data.nFileSizeHigh<<32)|data.nFileSizeLow, buffer, MAX_PATH);
645             break;
646         case COLUMN_MTIME:
647             FormatDateTime(buffer, MAX_PATH, data.ftLastWriteTime);
648             break;
649         case COLUMN_TYPE:
650             /* TODO */
651             buffer[0] = 0;
652             break;
653         default:
654             return E_FAIL;
655     }
656     
657     pDetails->str.uType = STRRET_WSTR;
658     return SHStrDupW(buffer, &pDetails->str.u.pOleStr);
659 }
660
661 static HRESULT WINAPI RecycleBin_MapColumnToSCID(IShellFolder2 *iface, UINT iColumn, SHCOLUMNID *pscid)
662 {
663     RecycleBin *This = impl_from_IShellFolder2(iface);
664     TRACE("(%p, %d, %p)\n", This, iColumn, pscid);
665     if (iColumn>=COLUMNS_COUNT)
666         return E_INVALIDARG;
667     pscid->fmtid = *RecycleBinColumns[iColumn].fmtId;
668     pscid->pid = RecycleBinColumns[iColumn].pid;
669     return S_OK;
670 }
671
672 static const IShellFolder2Vtbl recycleBinVtbl = 
673 {
674     /* IUnknown */
675     RecycleBin_QueryInterface,
676     RecycleBin_AddRef,
677     RecycleBin_Release,
678
679     /* IShellFolder */
680     RecycleBin_ParseDisplayName,
681     RecycleBin_EnumObjects,
682     RecycleBin_BindToObject,
683     RecycleBin_BindToStorage,
684     RecycleBin_CompareIDs,
685     RecycleBin_CreateViewObject,
686     RecycleBin_GetAttributesOf,
687     RecycleBin_GetUIObjectOf,
688     RecycleBin_GetDisplayNameOf,
689     RecycleBin_SetNameOf,
690
691     /* IShellFolder2 */
692     RecycleBin_GetDefaultSearchGUID,
693     RecycleBin_EnumSearches,
694     RecycleBin_GetDefaultColumn,
695     RecycleBin_GetDefaultColumnState,
696     RecycleBin_GetDetailsEx,
697     RecycleBin_GetDetailsOf,
698     RecycleBin_MapColumnToSCID
699 };
700
701 static HRESULT WINAPI RecycleBin_IPersistFolder2_QueryInterface(IPersistFolder2 *iface, REFIID riid,
702         void **ppvObject)
703 {
704     RecycleBin *This = impl_from_IPersistFolder2(iface);
705
706     return RecycleBin_QueryInterface(&This->IShellFolder2_iface, riid, ppvObject);
707 }
708
709 static ULONG WINAPI RecycleBin_IPersistFolder2_AddRef(IPersistFolder2 *iface)
710 {
711     RecycleBin *This = impl_from_IPersistFolder2(iface);
712
713     return RecycleBin_AddRef(&This->IShellFolder2_iface);
714 }
715
716 static ULONG WINAPI RecycleBin_IPersistFolder2_Release(IPersistFolder2 *iface)
717 {
718     RecycleBin *This = impl_from_IPersistFolder2(iface);
719
720     return RecycleBin_Release(&This->IShellFolder2_iface);
721 }
722
723 static const IPersistFolder2Vtbl recycleBinPersistVtbl =
724 {
725     /* IUnknown */
726     RecycleBin_IPersistFolder2_QueryInterface,
727     RecycleBin_IPersistFolder2_AddRef,
728     RecycleBin_IPersistFolder2_Release,
729
730     /* IPersist */
731     RecycleBin_GetClassID,
732     /* IPersistFolder */
733     RecycleBin_Initialize,
734     /* IPersistFolder2 */
735     RecycleBin_GetCurFolder
736 };
737
738 static HRESULT WINAPI RecycleBin_ISFHelper_QueryInterface(ISFHelper *iface, REFIID riid,
739         void **ppvObject)
740 {
741     RecycleBin *This = impl_from_ISFHelper(iface);
742
743     return RecycleBin_QueryInterface(&This->IShellFolder2_iface, riid, ppvObject);
744 }
745
746 static ULONG WINAPI RecycleBin_ISFHelper_AddRef(ISFHelper *iface)
747 {
748     RecycleBin *This = impl_from_ISFHelper(iface);
749
750     return RecycleBin_AddRef(&This->IShellFolder2_iface);
751 }
752
753 static ULONG WINAPI RecycleBin_ISFHelper_Release(ISFHelper *iface)
754 {
755     RecycleBin *This = impl_from_ISFHelper(iface);
756
757     return RecycleBin_Release(&This->IShellFolder2_iface);
758 }
759
760 static HRESULT WINAPI RecycleBin_GetUniqueName(ISFHelper *iface,LPWSTR lpName,
761                                                UINT  uLen)
762 {
763     return E_NOTIMPL;
764 }
765
766 static HRESULT WINAPI RecycleBin_AddFolder(ISFHelper * iface, HWND hwnd,
767                                            LPCWSTR pwszName,
768                                            LPITEMIDLIST * ppidlOut)
769 {
770     /*Adding folders doesn't make sense in the recycle bin*/
771     return E_NOTIMPL;
772 }
773
774 static HRESULT erase_items(HWND parent,const LPCITEMIDLIST * apidl, UINT cidl, BOOL confirm)
775 {
776     UINT i=0;
777     HRESULT ret = S_OK;
778     LPITEMIDLIST recyclebin;
779
780     if(confirm)
781     {
782         WCHAR arg[MAX_PATH];
783         WCHAR message[100];
784         WCHAR caption[50];
785         switch(cidl)
786         {
787         case 0:
788             return S_OK;
789         case 1:
790             {
791                 WIN32_FIND_DATAW data;
792                 TRASH_UnpackItemID(&((*apidl)->mkid),&data);
793                 lstrcpynW(arg,data.cFileName,MAX_PATH);
794                 LoadStringW(shell32_hInstance,IDS_RECYCLEBIN_ERASEITEM,message,
795                             sizeof(message)/sizeof(WCHAR));
796                 break;
797             }
798         default:
799             {
800                 static const WCHAR format[]={'%','u','\0'};
801                 LoadStringW(shell32_hInstance,IDS_RECYCLEBIN_ERASEMULTIPLE,
802                             message,sizeof(message)/sizeof(WCHAR));
803                 sprintfW(arg,format,cidl);
804                 break;
805             }
806
807         }
808         LoadStringW(shell32_hInstance,IDS_RECYCLEBIN_ERASE_CAPTION,caption,
809                     sizeof(caption)/sizeof(WCHAR));
810         if(ShellMessageBoxW(shell32_hInstance,parent,message,caption,
811                             MB_YESNO|MB_ICONEXCLAMATION,arg)!=IDYES)
812             return ret;
813
814     }
815     SHGetFolderLocation(parent,CSIDL_BITBUCKET,0,0,&recyclebin);
816     for (; i<cidl; i++)
817     {
818         if(SUCCEEDED(TRASH_EraseItem(apidl[i])))
819             SHChangeNotify(SHCNE_DELETE,SHCNF_IDLIST,
820                            ILCombine(recyclebin,apidl[i]),0);
821     }
822     ILFree(recyclebin);
823     return S_OK;
824 }
825
826 static HRESULT WINAPI RecycleBin_DeleteItems(ISFHelper * iface, UINT cidl,
827                                              LPCITEMIDLIST * apidl)
828 {
829     TRACE("(%p,%u,%p)\n",iface,cidl,apidl);
830     return erase_items(GetActiveWindow(),apidl,cidl,TRUE);
831 }
832
833 static HRESULT WINAPI RecycleBin_CopyItems(ISFHelper * iface,
834                                            IShellFolder * pSFFrom,
835                                            UINT cidl, LPCITEMIDLIST * apidl)
836 {
837     return E_NOTIMPL;
838 }
839
840 static const ISFHelperVtbl sfhelperVtbl =
841 {
842     RecycleBin_ISFHelper_QueryInterface,
843     RecycleBin_ISFHelper_AddRef,
844     RecycleBin_ISFHelper_Release,
845     RecycleBin_GetUniqueName,
846     RecycleBin_AddFolder,
847     RecycleBin_DeleteItems,
848     RecycleBin_CopyItems
849 };
850
851 HRESULT WINAPI SHQueryRecycleBinA(LPCSTR pszRootPath, LPSHQUERYRBINFO pSHQueryRBInfo)
852 {
853     WCHAR wszRootPath[MAX_PATH];
854     MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, wszRootPath, MAX_PATH);
855     return SHQueryRecycleBinW(wszRootPath, pSHQueryRBInfo);
856 }
857
858 HRESULT WINAPI SHQueryRecycleBinW(LPCWSTR pszRootPath, LPSHQUERYRBINFO pSHQueryRBInfo)
859 {
860     LPITEMIDLIST *apidl;
861     INT cidl;
862     INT i=0;
863     TRACE("(%s, %p)\n", debugstr_w(pszRootPath), pSHQueryRBInfo);
864     FIXME("Ignoring pszRootPath=%s\n",debugstr_w(pszRootPath));
865
866     TRASH_EnumItems(&apidl,&cidl);
867     pSHQueryRBInfo->i64NumItems = cidl;
868     pSHQueryRBInfo->i64Size = 0;
869     for (; i<cidl; i++)
870     {
871         WIN32_FIND_DATAW data;
872         TRASH_UnpackItemID(&((apidl[i])->mkid),&data);
873         pSHQueryRBInfo->i64Size += ((DWORDLONG)data.nFileSizeHigh << 32) + data.nFileSizeLow;
874         ILFree(apidl[i]);
875     }
876     SHFree(apidl);
877     return S_OK;
878 }
879
880 HRESULT WINAPI SHEmptyRecycleBinA(HWND hwnd, LPCSTR pszRootPath, DWORD dwFlags)
881 {
882     WCHAR wszRootPath[MAX_PATH];
883     MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, wszRootPath, MAX_PATH);
884     return SHEmptyRecycleBinW(hwnd, wszRootPath, dwFlags);
885 }
886
887 #define SHERB_NOCONFIRMATION 1
888 #define SHERB_NOPROGRESSUI   2
889 #define SHERB_NOSOUND        4
890
891 HRESULT WINAPI SHEmptyRecycleBinW(HWND hwnd, LPCWSTR pszRootPath, DWORD dwFlags)
892 {
893     LPITEMIDLIST *apidl;
894     INT cidl;
895     INT i=0;
896     HRESULT ret;
897     TRACE("(%p, %s, 0x%08x)\n", hwnd, debugstr_w(pszRootPath) , dwFlags);
898     FIXME("Ignoring pszRootPath=%s\n",debugstr_w(pszRootPath));
899     TRASH_EnumItems(&apidl,&cidl);
900     ret = erase_items(hwnd,(const LPCITEMIDLIST*)apidl,cidl,!(dwFlags & SHERB_NOCONFIRMATION));
901     for (;i<cidl;i++)
902         ILFree(apidl[i]);
903     SHFree(apidl);
904     return ret;
905 }
906
907 /*************************************************************************
908  * SHUpdateRecycleBinIcon                                [SHELL32.@]
909  *
910  * Undocumented
911  */
912 HRESULT WINAPI SHUpdateRecycleBinIcon(void)
913 {
914     FIXME("stub\n");
915     return S_OK;
916 }