1 /*
2  * PROJECT:     Recycle bin management
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Deals with a system-wide recycle bin
5  * COPYRIGHT:   Copyright 2007 Hervé Poussineau (hpoussin@reactos.org)
6  *              Copyright 2024 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
7  */
8 
9 #include "recyclebin_private.h"
10 
11 class RecycleBinGeneric : public IRecycleBin
12 {
13 public:
14     RecycleBinGeneric();
15     virtual ~RecycleBinGeneric();
16 
17     /* IUnknown methods */
18     STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) override;
19     STDMETHODIMP_(ULONG) AddRef() override;
20     STDMETHODIMP_(ULONG) Release() override;
21 
22     /* IRecycleBin methods */
23     STDMETHODIMP DeleteFile(LPCWSTR szFileName) override;
24     STDMETHODIMP EmptyRecycleBin() override;
25     STDMETHODIMP EnumObjects(IRecycleBinEnumList **ppEnumList) override;
26 
27 protected:
28     LONG m_ref;
29 };
30 
31 STDMETHODIMP RecycleBinGeneric::QueryInterface(REFIID riid, void **ppvObject)
32 {
33     TRACE("(%p, %s, %p)\n", this, debugstr_guid(&riid), ppvObject);
34 
35     if (!ppvObject)
36         return E_POINTER;
37 
38     if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IRecycleBin))
39         *ppvObject = static_cast<IRecycleBin *>(this);
40     else
41     {
42         *ppvObject = NULL;
43         return E_NOINTERFACE;
44     }
45 
46     AddRef();
47     return S_OK;
48 }
49 
50 STDMETHODIMP_(ULONG) RecycleBinGeneric::AddRef()
51 {
52     ULONG refCount = InterlockedIncrement(&m_ref);
53     TRACE("(%p)\n", this);
54     return refCount;
55 }
56 
57 RecycleBinGeneric::~RecycleBinGeneric()
58 {
59     TRACE("(%p)\n", this);
60 }
61 
62 STDMETHODIMP_(ULONG) RecycleBinGeneric::Release()
63 {
64     TRACE("(%p)\n", this);
65 
66     ULONG refCount = InterlockedDecrement(&m_ref);
67     if (refCount == 0)
68         delete this;
69     return refCount;
70 }
71 
72 STDMETHODIMP RecycleBinGeneric::DeleteFile(LPCWSTR szFileName)
73 {
74     TRACE("(%p, %s)\n", this, debugstr_w(szFileName));
75 
76     /* Get full file name */
77     LPWSTR szFullName = NULL;
78     DWORD dwBufferLength = 0;
79     while (TRUE)
80     {
81         DWORD len = GetFullPathNameW(szFileName, dwBufferLength, szFullName, NULL);
82         if (len == 0)
83         {
84             if (szFullName)
85                 CoTaskMemFree(szFullName);
86             return HRESULT_FROM_WIN32(GetLastError());
87         }
88         else if (len < dwBufferLength)
89             break;
90         if (szFullName)
91             CoTaskMemFree(szFullName);
92         dwBufferLength = len;
93         szFullName = (LPWSTR)CoTaskMemAlloc(dwBufferLength * sizeof(WCHAR));
94         if (!szFullName)
95             return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
96     }
97 
98     /* Get associated volume path */
99     WCHAR szVolume[MAX_PATH];
100 #ifndef __REACTOS__
101     if (!GetVolumePathNameW(szFullName, szVolume, _countof(szVolume)))
102     {
103         CoTaskMemFree(szFullName);
104         return HRESULT_FROM_WIN32(GetLastError());
105     }
106 #else
107     swprintf(szVolume, L"%c:\\", szFullName[0]);
108 #endif
109 
110     /* Skip namespace (if any): "\\.\" or "\\?\" */
111     if (szVolume[0] == '\\' &&
112         szVolume[1] == '\\' &&
113         (szVolume[2] == '.' || szVolume[2] == '?') &&
114         szVolume[3] == '\\')
115     {
116         MoveMemory(szVolume, &szVolume[4], (_countof(szVolume) - 4) * sizeof(WCHAR));
117     }
118 
119     IRecycleBin *prb;
120     HRESULT hr = GetDefaultRecycleBin(szVolume, &prb);
121     if (!SUCCEEDED(hr))
122     {
123         CoTaskMemFree(szFullName);
124         return hr;
125     }
126 
127     hr = prb->DeleteFile(szFullName);
128     CoTaskMemFree(szFullName);
129     prb->Release();
130     return hr;
131 }
132 
133 STDMETHODIMP RecycleBinGeneric::EmptyRecycleBin()
134 {
135     TRACE("(%p)\n", this);
136 
137     DWORD dwLogicalDrives = GetLogicalDrives();
138     if (dwLogicalDrives == 0)
139         return HRESULT_FROM_WIN32(GetLastError());
140 
141     for (DWORD i = 0; i < 'Z' - 'A' + 1; i++)
142     {
143         if (!(dwLogicalDrives & (1 << i)))
144             continue;
145 
146         WCHAR szVolumeName[MAX_PATH];
147         swprintf(szVolumeName, L"%c:\\", L'A' + i);
148         if (GetDriveTypeW(szVolumeName) != DRIVE_FIXED)
149             continue;
150 
151         IRecycleBin *prb;
152         HRESULT hr = GetDefaultRecycleBin(szVolumeName, &prb);
153         if (!SUCCEEDED(hr))
154             return hr;
155 
156         hr = prb->EmptyRecycleBin();
157         prb->Release();
158     }
159 
160     return S_OK;
161 }
162 
163 STDMETHODIMP RecycleBinGeneric::EnumObjects(IRecycleBinEnumList **ppEnumList)
164 {
165     TRACE("(%p, %p)\n", this, ppEnumList);
166     return RecycleBinGenericEnum_Constructor(ppEnumList);
167 }
168 
169 RecycleBinGeneric::RecycleBinGeneric()
170     : m_ref(1)
171 {
172 }
173 
174 EXTERN_C
175 HRESULT RecycleBinGeneric_Constructor(OUT IUnknown **ppUnknown)
176 {
177     /* This RecycleBin implementation was introduced to be able to manage all
178      * drives at once, and instanciate the 'real' implementations when needed */
179     RecycleBinGeneric *pThis = new RecycleBinGeneric();
180     if (!pThis)
181         return E_OUTOFMEMORY;
182 
183     *ppUnknown = static_cast<IRecycleBin *>(pThis);
184     return S_OK;
185 }
186