1 /*
2  * PROJECT:     Recycle bin management
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Enumerates contents of all recycle bins
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 RecycleBinGenericEnum : public IRecycleBinEnumList
12 {
13 public:
14     RecycleBinGenericEnum();
15     virtual ~RecycleBinGenericEnum();
16 
17     /* IUnknown methods */
18     STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) override;
19     STDMETHODIMP_(ULONG) AddRef() override;
20     STDMETHODIMP_(ULONG) Release() override;
21 
22     /* IRecycleBinEnumList methods */
23     STDMETHODIMP Next(DWORD celt, IRecycleBinFile **rgelt, DWORD *pceltFetched) override;
24     STDMETHODIMP Skip(DWORD celt) override;
25     STDMETHODIMP Reset() override;
26 
27 protected:
28     LONG m_ref;
29     IRecycleBinEnumList *m_current;
30     DWORD m_dwLogicalDrives;
31     SIZE_T m_skip;
32 };
33 
34 STDMETHODIMP
QueryInterface(REFIID riid,void ** ppvObject)35 RecycleBinGenericEnum::QueryInterface(REFIID riid, void **ppvObject)
36 {
37     TRACE("(%p, %s, %p)\n", this, debugstr_guid(&riid), ppvObject);
38 
39     if (!ppvObject)
40         return E_POINTER;
41 
42     if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IRecycleBinEnumList))
43         *ppvObject = static_cast<IRecycleBinEnumList *>(this);
44     else
45     {
46         *ppvObject = NULL;
47         return E_NOINTERFACE;
48     }
49 
50     AddRef();
51     return S_OK;
52 }
53 
STDMETHODIMP_(ULONG)54 STDMETHODIMP_(ULONG)
55 RecycleBinGenericEnum::AddRef()
56 {
57     ULONG refCount = InterlockedIncrement(&m_ref);
58     TRACE("(%p)\n", this);
59     return refCount;
60 }
61 
~RecycleBinGenericEnum()62 RecycleBinGenericEnum::~RecycleBinGenericEnum()
63 {
64     TRACE("(%p)\n", this);
65 
66     if (m_current)
67         m_current->Release();
68 }
69 
STDMETHODIMP_(ULONG)70 STDMETHODIMP_(ULONG)
71 RecycleBinGenericEnum::Release()
72 {
73     TRACE("(%p)\n", this);
74 
75     ULONG refCount = InterlockedDecrement(&m_ref);
76     if (refCount == 0)
77         delete this;
78     return refCount;
79 }
80 
81 STDMETHODIMP
Next(DWORD celt,IRecycleBinFile ** rgelt,DWORD * pceltFetched)82 RecycleBinGenericEnum::Next(DWORD celt, IRecycleBinFile **rgelt, DWORD *pceltFetched)
83 {
84     TRACE("(%p, %u, %p, %p)\n", this, celt, rgelt, pceltFetched);
85 
86     if (!rgelt)
87         return E_POINTER;
88     if (!pceltFetched && celt > 1)
89         return E_INVALIDARG;
90 
91     HRESULT hr;
92     DWORD fetched = 0;
93     while (TRUE)
94     {
95         /* Get enumerator implementation */
96         if (!m_current && m_dwLogicalDrives)
97         {
98             for (DWORD i = 0; i < L'Z' - L'A' + 1; ++i)
99             {
100                 if (m_dwLogicalDrives & (1 << i))
101                 {
102                     WCHAR szVolumeName[4];
103                     szVolumeName[0] = (WCHAR)(L'A' + i);
104                     szVolumeName[1] = L':';
105                     szVolumeName[2] = L'\\';
106                     szVolumeName[3] = UNICODE_NULL;
107                     if (GetDriveTypeW(szVolumeName) != DRIVE_FIXED)
108                     {
109                         m_dwLogicalDrives &= ~(1 << i);
110                         continue;
111                     }
112 
113                     IRecycleBin *prb;
114                     hr = GetDefaultRecycleBin(szVolumeName, &prb);
115                     if (!SUCCEEDED(hr))
116                         return hr;
117                     hr = prb->EnumObjects(&m_current);
118                     prb->Release();
119 
120                     if (!SUCCEEDED(hr))
121                         return hr;
122 
123                     m_dwLogicalDrives &= ~(1 << i);
124                     break;
125                 }
126             }
127         }
128 
129         if (!m_current)
130         {
131             /* Nothing more to enumerate */
132             if (pceltFetched)
133                 *pceltFetched = fetched;
134             return S_FALSE;
135         }
136 
137         /* Skip some elements */
138         while (m_skip > 0)
139         {
140             IRecycleBinFile *rbf;
141             hr = m_current->Next(1, &rbf, NULL);
142             if (hr == S_OK)
143                 rbf->Release();
144             else if (hr == S_FALSE)
145                 break;
146             else if (!SUCCEEDED(hr))
147                 return hr;
148         }
149 
150         if (m_skip > 0)
151             continue;
152 
153         /* Fill area */
154         DWORD newFetched;
155         hr = m_current->Next(celt - fetched, &rgelt[fetched], &newFetched);
156         if (SUCCEEDED(hr))
157             fetched += newFetched;
158 
159         if (hr == S_FALSE || newFetched == 0)
160         {
161             m_current->Release();
162             m_current = NULL;
163         }
164         else if (!SUCCEEDED(hr))
165             return hr;
166 
167         if (fetched == celt)
168         {
169             if (pceltFetched)
170                 *pceltFetched = fetched;
171             return S_OK;
172         }
173     }
174 
175     /* Never go here */
176     UNREACHABLE;
177 }
178 
Skip(DWORD celt)179 STDMETHODIMP RecycleBinGenericEnum::Skip(DWORD celt)
180 {
181     TRACE("(%p, %u)\n", this, celt);
182     m_skip += celt;
183     return S_OK;
184 }
185 
Reset()186 STDMETHODIMP RecycleBinGenericEnum::Reset()
187 {
188     TRACE("(%p)\n", this);
189 
190     if (m_current)
191     {
192         m_current->Release();
193         m_current = NULL;
194     }
195     m_skip = 0;
196     m_dwLogicalDrives = ::GetLogicalDrives();
197     return S_OK;
198 }
199 
RecycleBinGenericEnum()200 RecycleBinGenericEnum::RecycleBinGenericEnum()
201     : m_ref(1)
202     , m_current(NULL)
203     , m_dwLogicalDrives(0)
204     , m_skip(0)
205 {
206 }
207 
208 EXTERN_C
209 HRESULT
RecycleBinGenericEnum_Constructor(OUT IRecycleBinEnumList ** pprbel)210 RecycleBinGenericEnum_Constructor(
211     OUT IRecycleBinEnumList **pprbel)
212 {
213     RecycleBinGenericEnum *pThis = new RecycleBinGenericEnum();
214     if (!pThis)
215         return E_OUTOFMEMORY;
216 
217     *pprbel = static_cast<IRecycleBinEnumList *>(pThis);
218     return (*pprbel)->Reset();
219 }
220