1 /*
2  * PROJECT:     Recycle bin management
3  * LICENSE:     GPL v2 - See COPYING in the top level directory
4  * FILE:        lib/recyclebin/recyclebin.c
5  * PURPOSE:     Public interface
6  * PROGRAMMERS: Copyright 2006-2007 Herv� Poussineau (hpoussin@reactos.org)
7  */
8 
9 #include "recyclebin_private.h"
10 
11 BOOL WINAPI
12 CloseRecycleBinHandle(
13     IN HANDLE hDeletedFile)
14 {
15     IRecycleBinFile *rbf = (IRecycleBinFile *)hDeletedFile;
16     HRESULT hr;
17 
18     TRACE("(%p)\n", hDeletedFile);
19 
20     hr = IRecycleBinFile_Release(rbf);
21     if (SUCCEEDED(hr))
22         return TRUE;
23     if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
24         SetLastError(HRESULT_CODE(hr));
25     else
26         SetLastError(ERROR_GEN_FAILURE);
27     return FALSE;
28 }
29 
30 BOOL WINAPI
31 DeleteFileToRecycleBinA(
32     IN LPCSTR FileName)
33 {
34     int len;
35     LPWSTR FileNameW = NULL;
36     BOOL ret = FALSE;
37 
38     TRACE("(%s)\n", debugstr_a(FileName));
39 
40     /* Check parameters */
41     if (FileName == NULL)
42     {
43         SetLastError(ERROR_INVALID_PARAMETER);
44         goto cleanup;
45     }
46 
47     len = MultiByteToWideChar(CP_ACP, 0, FileName, -1, NULL, 0);
48     if (len == 0)
49         goto cleanup;
50     FileNameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
51     if (!FileNameW)
52     {
53         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
54         goto cleanup;
55     }
56     if (MultiByteToWideChar(CP_ACP, 0, FileName, -1, FileNameW, len) == 0)
57         goto cleanup;
58 
59     ret = DeleteFileToRecycleBinW(FileNameW);
60 
61 cleanup:
62     HeapFree(GetProcessHeap(), 0, FileNameW);
63     return ret;
64 }
65 
66 BOOL WINAPI
67 DeleteFileToRecycleBinW(
68     IN LPCWSTR FileName)
69 {
70     IRecycleBin *prb;
71     HRESULT hr;
72 
73     TRACE("(%s)\n", debugstr_w(FileName));
74 
75     hr = GetDefaultRecycleBin(NULL, &prb);
76     if (!SUCCEEDED(hr))
77         goto cleanup;
78 
79     hr = IRecycleBin_DeleteFile(prb, FileName);
80     IRecycleBin_Release(prb);
81 
82 cleanup:
83     if (SUCCEEDED(hr))
84         return TRUE;
85     if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
86         SetLastError(HRESULT_CODE(hr));
87     else
88         SetLastError(ERROR_GEN_FAILURE);
89     return FALSE;
90 }
91 
92 BOOL WINAPI
93 DeleteFileHandleToRecycleBin(
94     IN HANDLE hDeletedFile)
95 {
96     IRecycleBinFile *rbf = (IRecycleBinFile *)hDeletedFile;
97     HRESULT hr;
98 
99     TRACE("(%p)\n", hDeletedFile);
100 
101     hr = IRecycleBinFile_Delete(rbf);
102 
103     if (SUCCEEDED(hr))
104         return TRUE;
105     if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
106         SetLastError(HRESULT_CODE(hr));
107     else
108         SetLastError(ERROR_GEN_FAILURE);
109     return FALSE;
110 }
111 
112 BOOL WINAPI
113 EmptyRecycleBinA(
114     IN LPCSTR pszRoot OPTIONAL)
115 {
116     int len;
117     LPWSTR szRootW = NULL;
118     BOOL ret = FALSE;
119 
120     TRACE("(%s)\n", debugstr_a(pszRoot));
121 
122     if (pszRoot)
123     {
124         len = MultiByteToWideChar(CP_ACP, 0, pszRoot, -1, NULL, 0);
125         if (len == 0)
126             goto cleanup;
127         szRootW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
128         if (!szRootW)
129         {
130             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
131             goto cleanup;
132         }
133         if (MultiByteToWideChar(CP_ACP, 0, pszRoot, -1, szRootW, len) == 0)
134             goto cleanup;
135     }
136 
137     ret = EmptyRecycleBinW(szRootW);
138 
139 cleanup:
140     HeapFree(GetProcessHeap(), 0, szRootW);
141     return ret;
142 }
143 
144 BOOL WINAPI
145 EmptyRecycleBinW(
146     IN LPCWSTR pszRoot OPTIONAL)
147 {
148     IRecycleBin *prb;
149     HRESULT hr;
150 
151     TRACE("(%s)\n", debugstr_w(pszRoot));
152 
153     hr = GetDefaultRecycleBin(pszRoot, &prb);
154     if (!SUCCEEDED(hr))
155         goto cleanup;
156 
157     hr = IRecycleBin_EmptyRecycleBin(prb);
158     IRecycleBin_Release(prb);
159 
160 cleanup:
161     if (SUCCEEDED(hr))
162         return TRUE;
163     if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
164         SetLastError(HRESULT_CODE(hr));
165     else
166         SetLastError(ERROR_GEN_FAILURE);
167     return FALSE;
168 }
169 
170 BOOL WINAPI
171 EnumerateRecycleBinA(
172     IN LPCSTR pszRoot OPTIONAL,
173     IN PENUMERATE_RECYCLEBIN_CALLBACK pFnCallback,
174     IN PVOID Context OPTIONAL)
175 {
176     int len;
177     LPWSTR szRootW = NULL;
178     BOOL ret = FALSE;
179 
180     TRACE("(%s, %p, %p)\n", debugstr_a(pszRoot), pFnCallback, Context);
181 
182     if (pszRoot)
183     {
184         len = MultiByteToWideChar(CP_ACP, 0, pszRoot, -1, NULL, 0);
185         if (len == 0)
186             goto cleanup;
187         szRootW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
188         if (!szRootW)
189         {
190             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
191             goto cleanup;
192         }
193         if (MultiByteToWideChar(CP_ACP, 0, pszRoot, -1, szRootW, len) == 0)
194             goto cleanup;
195     }
196 
197     ret = EnumerateRecycleBinW(szRootW, pFnCallback, Context);
198 
199 cleanup:
200     HeapFree(GetProcessHeap(), 0, szRootW);
201     return ret;
202 }
203 
204 BOOL WINAPI
205 EnumerateRecycleBinW(
206     IN LPCWSTR pszRoot OPTIONAL,
207     IN PENUMERATE_RECYCLEBIN_CALLBACK pFnCallback,
208     IN PVOID Context OPTIONAL)
209 {
210     IRecycleBin *prb = NULL;
211     IRecycleBinEnumList *prbel = NULL;
212     IRecycleBinFile *prbf;
213     HRESULT hr;
214 
215     TRACE("(%s, %p, %p)\n", debugstr_w(pszRoot), pFnCallback, Context);
216 
217     hr = GetDefaultRecycleBin(NULL, &prb);
218     if (!SUCCEEDED(hr))
219         goto cleanup;
220 
221     hr = IRecycleBin_EnumObjects(prb, &prbel);
222     if (!SUCCEEDED(hr))
223         goto cleanup;
224     while (TRUE)
225     {
226         hr = IRecycleBinEnumList_Next(prbel, 1, &prbf, NULL);
227         if (hr == S_FALSE)
228         {
229             hr = S_OK;
230             goto cleanup;
231         }
232         else if (!SUCCEEDED(hr))
233             goto cleanup;
234         if (!pFnCallback(Context, (HANDLE)prbf))
235         {
236             hr = HRESULT_FROM_WIN32(GetLastError());
237             goto cleanup;
238         }
239     }
240 
241 cleanup:
242     if (prb)
243         IRecycleBin_Release(prb);
244     if (prbel)
245         IRecycleBinEnumList_Release(prbel);
246     if (SUCCEEDED(hr))
247         return TRUE;
248     if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
249         SetLastError(HRESULT_CODE(hr));
250     else
251         SetLastError(ERROR_GEN_FAILURE);
252     return FALSE;
253 }
254 
255 BOOL WINAPI
256 GetDeletedFileDetailsA(
257     IN HANDLE hDeletedFile,
258     IN DWORD BufferSize,
259     IN OUT PDELETED_FILE_DETAILS_A FileDetails OPTIONAL,
260     OUT LPDWORD RequiredSize OPTIONAL)
261 {
262     PDELETED_FILE_DETAILS_W FileDetailsW = NULL;
263     DWORD BufferSizeW = 0;
264     BOOL ret = FALSE;
265 
266     TRACE("(%p, %lu, %p, %p)\n", hDeletedFile, BufferSize, FileDetails, RequiredSize);
267 
268     if (BufferSize >= FIELD_OFFSET(DELETED_FILE_DETAILS_A, FileName))
269     {
270         BufferSizeW = FIELD_OFFSET(DELETED_FILE_DETAILS_W, FileName)
271             + (BufferSize - FIELD_OFFSET(DELETED_FILE_DETAILS_A, FileName)) * sizeof(WCHAR);
272     }
273     if (FileDetails && BufferSizeW)
274     {
275         FileDetailsW = HeapAlloc(GetProcessHeap(), 0, BufferSizeW);
276         if (!FileDetailsW)
277         {
278             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
279             goto cleanup;
280         }
281     }
282 
283     ret = GetDeletedFileDetailsW(hDeletedFile, BufferSizeW, FileDetailsW, RequiredSize);
284     if (!ret)
285         goto cleanup;
286 
287     if (FileDetails)
288     {
289         CopyMemory(FileDetails, FileDetailsW, FIELD_OFFSET(DELETED_FILE_DETAILS_A, FileName));
290         if (0 == WideCharToMultiByte(CP_ACP, 0, FileDetailsW->FileName, -1, FileDetails->FileName, BufferSize - FIELD_OFFSET(DELETED_FILE_DETAILS_A, FileName), NULL, NULL))
291             goto cleanup;
292     }
293     ret = TRUE;
294 
295 cleanup:
296     HeapFree(GetProcessHeap(), 0, FileDetailsW);
297     return ret;
298 }
299 
300 BOOL WINAPI
301 GetDeletedFileDetailsW(
302     IN HANDLE hDeletedFile,
303     IN DWORD BufferSize,
304     IN OUT PDELETED_FILE_DETAILS_W FileDetails OPTIONAL,
305     OUT LPDWORD RequiredSize OPTIONAL)
306 {
307     IRecycleBinFile *rbf = (IRecycleBinFile *)hDeletedFile;
308     HRESULT hr;
309     SIZE_T NameSize, Needed;
310 
311     TRACE("(%p, %lu, %p, %p)\n", hDeletedFile, BufferSize, FileDetails, RequiredSize);
312 
313     hr = IRecycleBinFile_GetFileName(rbf, 0, NULL, &NameSize);
314     if (!SUCCEEDED(hr))
315         goto cleanup;
316     Needed = FIELD_OFFSET(DELETED_FILE_DETAILS_W, FileName) + NameSize;
317     if (RequiredSize)
318         *RequiredSize = (DWORD)Needed;
319     if (Needed > BufferSize)
320     {
321         hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
322         goto cleanup;
323     }
324     hr = IRecycleBinFile_GetFileName(rbf, NameSize, FileDetails->FileName, NULL);
325     if (!SUCCEEDED(hr))
326         goto cleanup;
327     hr = IRecycleBinFile_GetLastModificationTime(rbf, &FileDetails->LastModification);
328     if (!SUCCEEDED(hr))
329         goto cleanup;
330     hr = IRecycleBinFile_GetDeletionTime(rbf, &FileDetails->DeletionTime);
331     if (!SUCCEEDED(hr))
332         goto cleanup;
333     hr = IRecycleBinFile_GetFileSize(rbf, &FileDetails->FileSize);
334     if (!SUCCEEDED(hr))
335         goto cleanup;
336     hr = IRecycleBinFile_GetPhysicalFileSize(rbf, &FileDetails->PhysicalFileSize);
337     if (!SUCCEEDED(hr))
338         goto cleanup;
339     hr = IRecycleBinFile_GetAttributes(rbf, &FileDetails->Attributes);
340     if (!SUCCEEDED(hr))
341         goto cleanup;
342 
343 cleanup:
344     if (SUCCEEDED(hr))
345         return TRUE;
346     if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
347         SetLastError(HRESULT_CODE(hr));
348     else
349         SetLastError(ERROR_GEN_FAILURE);
350     return FALSE;
351 }
352 
353 BOOL WINAPI
354 GetRecycleBinDetails(
355     IN LPCWSTR pszVolume OPTIONAL,
356     OUT ULARGE_INTEGER *pulTotalItems,
357     OUT ULARGE_INTEGER *pulTotalSize)
358 {
359     pulTotalItems->QuadPart = 0;
360     pulTotalSize->QuadPart = 0;
361     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
362     return FALSE;
363 }
364 
365 BOOL WINAPI
366 RestoreFile(
367     IN HANDLE hDeletedFile)
368 {
369     IRecycleBinFile *rbf = (IRecycleBinFile *)hDeletedFile;
370     HRESULT hr;
371 
372     TRACE("(%p)\n", hDeletedFile);
373 
374     hr = IRecycleBinFile_Restore(rbf);
375     if (SUCCEEDED(hr))
376         return TRUE;
377     if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
378         SetLastError(HRESULT_CODE(hr));
379     else
380         SetLastError(ERROR_GEN_FAILURE);
381     return FALSE;
382 }
383 
384 HRESULT WINAPI
385 GetDefaultRecycleBin(
386     IN LPCWSTR pszVolume OPTIONAL,
387     OUT IRecycleBin **pprb)
388 {
389     IUnknown *pUnk;
390     HRESULT hr;
391 
392     TRACE("(%s, %p)\n", debugstr_w(pszVolume), pprb);
393 
394     if (!pprb)
395         return E_POINTER;
396 
397     if (!pszVolume)
398         hr = RecycleBinGeneric_Constructor(&pUnk);
399     else
400     {
401         /* FIXME: do a better validation! */
402         if (wcslen(pszVolume) != 3 || pszVolume[1] != ':' || pszVolume[2] != '\\')
403             return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
404 
405         /* For now, only support this type of recycle bins... */
406         hr = RecycleBin5_Constructor(pszVolume, &pUnk);
407     }
408     if (!SUCCEEDED(hr))
409         return hr;
410     hr = IUnknown_QueryInterface(pUnk, &IID_IRecycleBin, (void **)pprb);
411     IUnknown_Release(pUnk);
412     return hr;
413 }
414