1 /*
2  * Trash virtual folder support. The trashing engine is implemented in trash.c
3  *
4  * Copyright (C) 2006 Mikolaj Zalewski
5  * Copyright (C) 2009 Andrew Hill
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 <precomp.h>
23 
24 WINE_DEFAULT_DEBUG_CHANNEL(CRecycleBin);
25 
26 typedef struct
27 {
28     ULARGE_INTEGER FreeBytesAvailable;
29     DWORD dwSerial;
30     DWORD dwMaxCapacity;
31     DWORD dwNukeOnDelete;
32 } DRIVE_ITEM_CONTEXT, *PDRIVE_ITEM_CONTEXT;
33 
toggleNukeOnDeleteOption(HWND hwndDlg,BOOL bEnable)34 static void toggleNukeOnDeleteOption(HWND hwndDlg, BOOL bEnable)
35 {
36     if (bEnable)
37     {
38         SendDlgItemMessage(hwndDlg, 14001, BM_SETCHECK, BST_UNCHECKED, 0);
39         EnableWindow(GetDlgItem(hwndDlg, 14002), FALSE);
40         SendDlgItemMessage(hwndDlg, 14003, BM_SETCHECK, BST_CHECKED, 0);
41     }
42     else
43     {
44         SendDlgItemMessage(hwndDlg, 14001, BM_SETCHECK, BST_CHECKED, 0);
45         EnableWindow(GetDlgItem(hwndDlg, 14002), TRUE);
46         SendDlgItemMessage(hwndDlg, 14003, BM_SETCHECK, BST_UNCHECKED, 0);
47     }
48 
49     // FIXME: Max capacity not implemented yet, disable for now (CORE-13743)
50     EnableWindow(GetDlgItem(hwndDlg, 14002), FALSE);
51 }
52 
53 static VOID
InitializeRecycleBinDlg(HWND hwndDlg,WCHAR DefaultDrive)54 InitializeRecycleBinDlg(HWND hwndDlg, WCHAR DefaultDrive)
55 {
56     WCHAR szDrive[] = L"A:\\";
57     DWORD dwDrives;
58     WCHAR szName[100];
59     WCHAR szVolume[100];
60     DWORD MaxComponent, Flags;
61     DWORD dwSerial;
62     LVCOLUMNW lc;
63     HWND hDlgCtrl;
64     LVITEMW li;
65     INT itemCount;
66     ULARGE_INTEGER TotalNumberOfFreeBytes, TotalNumberOfBytes, FreeBytesAvailable;
67     RECT rect;
68     int columnSize;
69     int defIndex = 0;
70     DWORD dwSize;
71     PDRIVE_ITEM_CONTEXT pItem = NULL, pDefault = NULL, pFirst = NULL;
72 
73     hDlgCtrl = GetDlgItem(hwndDlg, 14000);
74 
75     if (!LoadStringW(shell32_hInstance, IDS_RECYCLEBIN_LOCATION, szVolume, _countof(szVolume)))
76         szVolume[0] = 0;
77 
78     GetClientRect(hDlgCtrl, &rect);
79 
80     ZeroMemory(&lc, sizeof(lc));
81     lc.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM | LVCF_FMT;
82 
83     columnSize = 140; //FIXME
84     lc.iSubItem   = 0;
85     lc.fmt = LVCFMT_FIXED_WIDTH;
86     lc.cx         = columnSize;
87     lc.cchTextMax = wcslen(szVolume);
88     lc.pszText    = szVolume;
89     (void)SendMessageW(hDlgCtrl, LVM_INSERTCOLUMNW, 0, (LPARAM)&lc);
90 
91     if (!LoadStringW(shell32_hInstance, IDS_RECYCLEBIN_DISKSPACE, szVolume, _countof(szVolume)))
92         szVolume[0] = 0;
93 
94     lc.iSubItem   = 1;
95     lc.cx         = rect.right - rect.left - columnSize;
96     lc.cchTextMax = wcslen(szVolume);
97     lc.pszText    = szVolume;
98     (void)SendMessageW(hDlgCtrl, LVM_INSERTCOLUMNW, 1, (LPARAM)&lc);
99 
100     dwDrives = GetLogicalDrives();
101     itemCount = 0;
102     do
103     {
104         if (dwDrives & 0x1)
105         {
106             UINT Type = GetDriveTypeW(szDrive);
107             if (Type == DRIVE_FIXED) //FIXME
108             {
109                 if (!GetVolumeInformationW(szDrive, szName, _countof(szName), &dwSerial, &MaxComponent, &Flags, NULL, 0))
110                 {
111                     szName[0] = 0;
112                     dwSerial = -1;
113                 }
114 
115                 swprintf(szVolume, L"%s (%c:)", szName, szDrive[0]);
116                 ZeroMemory(&li, sizeof(li));
117                 li.mask = LVIF_TEXT | LVIF_PARAM;
118                 li.iSubItem = 0;
119                 li.pszText = szVolume;
120                 li.iItem = itemCount;
121                 SendMessageW(hDlgCtrl, LVM_INSERTITEMW, 0, (LPARAM)&li);
122                 if (GetDiskFreeSpaceExW(szDrive, &FreeBytesAvailable, &TotalNumberOfBytes, &TotalNumberOfFreeBytes))
123                 {
124                     if (StrFormatByteSizeW(TotalNumberOfFreeBytes.QuadPart, szVolume, _countof(szVolume)))
125                     {
126                         pItem = (DRIVE_ITEM_CONTEXT *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DRIVE_ITEM_CONTEXT));
127                         if (pItem)
128                         {
129                             pItem->FreeBytesAvailable = FreeBytesAvailable;
130                             pItem->dwSerial = dwSerial;
131 
132                             swprintf(szName, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\BitBucket\\Volume\\%04X-%04X", LOWORD(dwSerial), HIWORD(dwSerial));
133 
134                             dwSize = sizeof(DWORD);
135                             if (RegGetValueW(HKEY_CURRENT_USER, szName, L"MaxCapacity", RRF_RT_DWORD, NULL, &pItem->dwMaxCapacity, &dwSize))
136                                 pItem->dwMaxCapacity = ~0;
137 
138                             /* Check if the maximum capacity doesn't exceed the available disk space (in megabytes), and truncate it if needed */
139                             FreeBytesAvailable.QuadPart = (FreeBytesAvailable.QuadPart / (1024 * 1024));
140                             pItem->dwMaxCapacity = min(pItem->dwMaxCapacity, FreeBytesAvailable.LowPart);
141 
142                             dwSize = sizeof(DWORD);
143                             RegGetValueW(HKEY_CURRENT_USER, szName, L"NukeOnDelete", RRF_RT_DWORD, NULL, &pItem->dwNukeOnDelete, &dwSize);
144 
145                             li.mask = LVIF_PARAM;
146                             li.lParam = (LPARAM)pItem;
147                             (void)SendMessageW(hDlgCtrl, LVM_SETITEMW, 0, (LPARAM)&li);
148                             if (szDrive[0] == DefaultDrive)
149                             {
150                                 defIndex = itemCount;
151                                 pDefault = pItem;
152                             }
153                         }
154                         if (!pFirst)
155                             pFirst = pItem;
156 
157                         li.mask = LVIF_TEXT;
158                         li.iSubItem = 1;
159                         li.pszText = szVolume;
160                         li.iItem = itemCount;
161                         (void)SendMessageW(hDlgCtrl, LVM_SETITEMW, 0, (LPARAM)&li);
162                     }
163                 }
164                 itemCount++;
165             }
166         }
167         szDrive[0]++;
168         dwDrives = (dwDrives >> 1);
169     } while (dwDrives);
170 
171     if (!pDefault)
172         pDefault = pFirst;
173     if (pDefault)
174     {
175         toggleNukeOnDeleteOption(hwndDlg, pDefault->dwNukeOnDelete);
176         SetDlgItemInt(hwndDlg, 14002, pDefault->dwMaxCapacity, FALSE);
177     }
178     ZeroMemory(&li, sizeof(li));
179     li.mask = LVIF_STATE;
180     li.stateMask = (UINT)-1;
181     li.state = LVIS_FOCUSED | LVIS_SELECTED;
182     li.iItem = defIndex;
183     (void)SendMessageW(hDlgCtrl, LVM_SETITEMW, 0, (LPARAM)&li);
184 }
185 
StoreDriveSettings(HWND hwndDlg)186 static BOOL StoreDriveSettings(HWND hwndDlg)
187 {
188     int iCount, iIndex;
189     HWND hDlgCtrl = GetDlgItem(hwndDlg, 14000);
190     LVITEMW li;
191     PDRIVE_ITEM_CONTEXT pItem;
192     HKEY hKey, hSubKey;
193     WCHAR szSerial[20];
194     DWORD dwSize;
195 
196     if (RegCreateKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\BitBucket\\Volume", 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL) != ERROR_SUCCESS)
197         return FALSE;
198 
199     iCount = ListView_GetItemCount(hDlgCtrl);
200 
201     ZeroMemory(&li, sizeof(li));
202     li.mask = LVIF_PARAM;
203 
204     for (iIndex = 0; iIndex < iCount; iIndex++)
205     {
206         li.iItem = iIndex;
207         if (SendMessageW(hDlgCtrl, LVM_GETITEMW, 0, (LPARAM)&li))
208         {
209             pItem = (PDRIVE_ITEM_CONTEXT)li.lParam;
210             swprintf(szSerial, L"%04X-%04X", LOWORD(pItem->dwSerial), HIWORD(pItem->dwSerial));
211             if (RegCreateKeyExW(hKey, szSerial, 0, NULL, 0, KEY_WRITE, NULL, &hSubKey, NULL) == ERROR_SUCCESS)
212             {
213                 dwSize = sizeof(DWORD);
214                 RegSetValueExW(hSubKey, L"MaxCapacity", 0, REG_DWORD, (LPBYTE)&pItem->dwMaxCapacity, dwSize);
215                 dwSize = sizeof(DWORD);
216                 RegSetValueExW(hSubKey, L"NukeOnDelete", 0, REG_DWORD, (LPBYTE)&pItem->dwNukeOnDelete, dwSize);
217                 RegCloseKey(hSubKey);
218             }
219         }
220     }
221     RegCloseKey(hKey);
222     return TRUE;
223 }
224 
FreeDriveItemContext(HWND hwndDlg)225 static VOID FreeDriveItemContext(HWND hwndDlg)
226 {
227     int iCount, iIndex;
228     HWND hDlgCtrl = GetDlgItem(hwndDlg, 14000);
229     LVITEMW li;
230 
231     iCount = ListView_GetItemCount(hDlgCtrl);
232 
233     ZeroMemory(&li, sizeof(li));
234     li.mask = LVIF_PARAM;
235 
236     for (iIndex = 0; iIndex < iCount; iIndex++)
237     {
238         li.iItem = iIndex;
239         if (SendMessageW(hDlgCtrl, LVM_GETITEMW, 0, (LPARAM)&li))
240         {
241             HeapFree(GetProcessHeap(), 0, (PVOID)li.lParam);
242         }
243     }
244 }
245 
246 static INT
GetSelectedDriveItem(HWND hwndDlg,LVITEMW * li)247 GetSelectedDriveItem(HWND hwndDlg, LVITEMW* li)
248 {
249     HWND hDlgCtrl;
250     UINT iItemCount, iIndex;
251 
252     hDlgCtrl = GetDlgItem(hwndDlg, 14000);
253     if (!hDlgCtrl)
254         return -1;
255 
256     iItemCount = ListView_GetItemCount(hDlgCtrl);
257     if (!iItemCount)
258         return -1;
259 
260     ZeroMemory(li, sizeof(LVITEMW));
261     li->mask = LVIF_PARAM | LVIF_STATE;
262     li->stateMask = (UINT)-1;
263     for (iIndex = 0; iIndex < iItemCount; iIndex++)
264     {
265         li->iItem = iIndex;
266         if (SendMessageW(hDlgCtrl, LVM_GETITEMW, 0, (LPARAM)li))
267         {
268             if (li->state & LVIS_SELECTED)
269                 return iIndex;
270         }
271     }
272     return -1;
273 }
274 
275 static INT_PTR CALLBACK
RecycleBinDlg(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)276 RecycleBinDlg(
277     HWND hwndDlg,
278     UINT uMsg,
279     WPARAM wParam,
280     LPARAM lParam)
281 {
282     enum { WM_NEWDRIVESELECTED = WM_APP, WM_UPDATEDRIVESETTINGS };
283     LPPSHNOTIFY lppsn;
284     LPNMLISTVIEW lppl;
285     LVITEMW li;
286     PDRIVE_ITEM_CONTEXT pItem;
287     BOOL bSuccess;
288     UINT uResult;
289     PROPSHEETPAGE * page;
290     DWORD dwStyle;
291     ULARGE_INTEGER FreeBytesAvailable;
292 
293     switch(uMsg)
294     {
295         case WM_INITDIALOG:
296             page = (PROPSHEETPAGE*)lParam;
297             InitializeRecycleBinDlg(hwndDlg, (WCHAR)page->lParam);
298             SendDlgItemMessage(hwndDlg, 14004, BM_SETCHECK, !SHELL_GetSetting(SSF_NOCONFIRMRECYCLE, fNoConfirmRecycle), 0);
299             dwStyle = (DWORD) SendDlgItemMessage(hwndDlg, 14000, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
300             dwStyle = dwStyle | LVS_EX_FULLROWSELECT;
301             SendDlgItemMessage(hwndDlg, 14000, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, dwStyle);
302             if (GetDlgCtrlID((HWND)wParam) != 14000)
303             {
304                 SetFocus(GetDlgItem(hwndDlg, 14000));
305                 return FALSE;
306             }
307             return TRUE;
308         case WM_COMMAND:
309             switch(LOWORD(wParam))
310             {
311                 case 14001:
312                     toggleNukeOnDeleteOption(hwndDlg, FALSE);
313                     PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
314                     break;
315                 case 14002:
316                     if (HIWORD(wParam) == EN_CHANGE)
317                         PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
318                     break;
319                 case 14003:
320                     toggleNukeOnDeleteOption(hwndDlg, TRUE);
321                     PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
322                     break;
323                 case 14004:
324                     PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
325                     break;
326             }
327             break;
328         case WM_NOTIFY:
329             lppsn = (LPPSHNOTIFY) lParam;
330             lppl = (LPNMLISTVIEW) lParam;
331             if (lppsn->hdr.code == PSN_APPLY)
332             {
333                 SHELLSTATE ss;
334                 ss.fNoConfirmRecycle = SendDlgItemMessage(hwndDlg, 14004, BM_GETCHECK, 0, 0) == BST_UNCHECKED;
335                 SHGetSetSettings(&ss, SSF_NOCONFIRMRECYCLE, TRUE);
336 
337                 if (GetSelectedDriveItem(hwndDlg, &li) > -1)
338                 {
339                     SendMessage(hwndDlg, WM_UPDATEDRIVESETTINGS, 0, li.lParam);
340                 }
341                 if (StoreDriveSettings(hwndDlg))
342                 {
343                     SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR );
344                     return TRUE;
345                 }
346             }
347             else if (lppl->hdr.code == LVN_ITEMCHANGING)
348             {
349                 ZeroMemory(&li, sizeof(li));
350                 li.mask = LVIF_PARAM;
351                 li.iItem = lppl->iItem;
352                 if (!SendMessageW(lppl->hdr.hwndFrom, LVM_GETITEMW, 0, (LPARAM)&li))
353                     return TRUE;
354 
355                 pItem = (PDRIVE_ITEM_CONTEXT)li.lParam;
356                 if (!pItem)
357                     return TRUE;
358 
359                 if (!(lppl->uOldState & LVIS_FOCUSED) && (lppl->uNewState & LVIS_FOCUSED))
360                 {
361                     // New focused item, delay handling until after kill focus has been processed
362                     PostMessage(hwndDlg, WM_NEWDRIVESELECTED, 0, (LPARAM)pItem);
363                 }
364                 else if ((lppl->uOldState & LVIS_FOCUSED) && !(lppl->uNewState & LVIS_FOCUSED))
365                 {
366                     // Kill focus
367                     SendMessage(hwndDlg, WM_UPDATEDRIVESETTINGS, 0, (LPARAM)pItem);
368                 }
369                 return TRUE;
370 
371             }
372             break;
373         case WM_NEWDRIVESELECTED:
374             if (lParam)
375             {
376                 pItem = (PDRIVE_ITEM_CONTEXT)lParam;
377                 toggleNukeOnDeleteOption(hwndDlg, pItem->dwNukeOnDelete);
378                 SetDlgItemInt(hwndDlg, 14002, pItem->dwMaxCapacity, FALSE);
379             }
380             break;
381         case WM_UPDATEDRIVESETTINGS:
382             if (lParam)
383             {
384                 pItem = (PDRIVE_ITEM_CONTEXT)lParam;
385                 uResult = GetDlgItemInt(hwndDlg, 14002, &bSuccess, FALSE);
386                 if (bSuccess)
387                 {
388                     /* Check if the maximum capacity doesn't exceed the available disk space (in megabytes), and truncate it if needed */
389                     FreeBytesAvailable = pItem->FreeBytesAvailable;
390                     FreeBytesAvailable.QuadPart = (FreeBytesAvailable.QuadPart / (1024 * 1024));
391                     pItem->dwMaxCapacity = min(uResult, FreeBytesAvailable.LowPart);
392                     SetDlgItemInt(hwndDlg, 14002, pItem->dwMaxCapacity, FALSE);
393                 }
394                 if (SendDlgItemMessageW(hwndDlg, 14003, BM_GETCHECK, 0, 0) == BST_CHECKED)
395                     pItem->dwNukeOnDelete = TRUE;
396                 else
397                     pItem->dwNukeOnDelete = FALSE;
398             }
399             break;
400         case WM_DESTROY:
401             FreeDriveItemContext(hwndDlg);
402             break;
403     }
404     return FALSE;
405 }
406 
RecycleBin_AddPropSheetPages(LPFNSVADDPROPSHEETPAGE pfnAddPage,LPARAM lParam)407 HRESULT RecycleBin_AddPropSheetPages(LPFNSVADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
408 {
409     HPROPSHEETPAGE hpsp = SH_CreatePropertySheetPage(IDD_RECYCLE_BIN_PROPERTIES, RecycleBinDlg, 0, NULL);
410     return AddPropSheetPage(hpsp, pfnAddPage, lParam);
411 }
412