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  * Copyright (C) 2017 Giannis Adamopoulos
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22 
23 #include <precomp.h>
24 
25 WINE_DEFAULT_DEBUG_CHANNEL (shell);
26 
27 class CRecyclerDropTarget :
28     public CComObjectRootEx<CComMultiThreadModelNoCS>,
29     public IDropTarget
30 {
31     private:
32         BOOL fAcceptFmt;       /* flag for pending Drop */
33         UINT cfShellIDList;
34 
_DoDeleteDataObject(IDataObject * pda,DWORD fMask)35         static HRESULT _DoDeleteDataObject(IDataObject *pda, DWORD fMask)
36         {
37             HRESULT hr;
38             STGMEDIUM medium;
39             FORMATETC formatetc;
40             InitFormatEtc (formatetc, CF_HDROP, TYMED_HGLOBAL);
41             hr = pda->GetData(&formatetc, &medium);
42             if (FAILED_UNEXPECTEDLY(hr))
43                 return hr;
44 
45             LPDROPFILES lpdf = (LPDROPFILES) GlobalLock(medium.hGlobal);
46             if (!lpdf)
47             {
48                 ERR("Error locking global\n");
49                 ReleaseStgMedium(&medium);
50                 return E_FAIL;
51             }
52 
53             /* Delete them */
54             SHFILEOPSTRUCTW FileOp;
55             ZeroMemory(&FileOp, sizeof(FileOp));
56             FileOp.wFunc = FO_DELETE;
57             FileOp.pFrom = (LPWSTR) (((byte*) lpdf) + lpdf->pFiles);;
58             if ((fMask & CMIC_MASK_SHIFT_DOWN) == 0)
59                 FileOp.fFlags = FOF_ALLOWUNDO;
60             TRACE("Deleting file (just the first) = %s, allowundo: %d\n", debugstr_w(FileOp.pFrom), (FileOp.fFlags == FOF_ALLOWUNDO));
61 
62             int res = SHFileOperationW(&FileOp);
63             if (res)
64             {
65                 ERR("SHFileOperation failed with 0x%x\n", res);
66                 hr = E_FAIL;
67             }
68 
69             GlobalUnlock(medium.hGlobal);
70             ReleaseStgMedium(&medium);
71 
72             return hr;
73         }
74 
75         struct DeleteThreadData {
76             IStream *s;
77             DWORD fMask;
78         };
79 
_DoDeleteThreadProc(LPVOID lpParameter)80         static DWORD WINAPI _DoDeleteThreadProc(LPVOID lpParameter)
81         {
82             DeleteThreadData *data = static_cast<DeleteThreadData*>(lpParameter);
83             CoInitialize(NULL);
84             IDataObject *pDataObject;
85             HRESULT hr = CoGetInterfaceAndReleaseStream (data->s, IID_PPV_ARG(IDataObject, &pDataObject));
86             if (SUCCEEDED(hr))
87             {
88                 _DoDeleteDataObject(pDataObject, data->fMask);
89             }
90             pDataObject->Release();
91             CoUninitialize();
92             HeapFree(GetProcessHeap(), 0, data);
93             return 0;
94         }
95 
_DoDeleteAsync(IDataObject * pda,DWORD fMask)96         static void _DoDeleteAsync(IDataObject *pda, DWORD fMask)
97         {
98             DeleteThreadData *data = static_cast<DeleteThreadData*>(HeapAlloc(GetProcessHeap(), 0, sizeof(DeleteThreadData)));
99             data->fMask = fMask;
100             CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pda, &data->s);
101             SHCreateThread(_DoDeleteThreadProc, data, NULL, NULL);
102         }
103 
104     public:
CRecyclerDropTarget()105         CRecyclerDropTarget()
106         {
107             fAcceptFmt = FALSE;
108             cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
109         }
110 
111         STDMETHODIMP
DragEnter(IDataObject * pDataObject,DWORD dwKeyState,POINTL pt,DWORD * pdwEffect)112         DragEnter(IDataObject *pDataObject, DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) override
113         {
114             TRACE("Recycle bin drag over (%p)\n", this);
115             /* The recycle bin accepts pretty much everything, and sets a CSIDL flag. */
116             fAcceptFmt = TRUE;
117 
118             *pdwEffect = DROPEFFECT_MOVE;
119             return S_OK;
120         }
121 
122         STDMETHODIMP
DragOver(DWORD dwKeyState,POINTL pt,DWORD * pdwEffect)123         DragOver(DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) override
124         {
125             TRACE("(%p)\n", this);
126 
127             if (!pdwEffect)
128                 return E_INVALIDARG;
129 
130             *pdwEffect = DROPEFFECT_MOVE;
131 
132             return S_OK;
133         }
134 
135         STDMETHODIMP
DragLeave()136         DragLeave() override
137         {
138             TRACE("(%p)\n", this);
139 
140             fAcceptFmt = FALSE;
141 
142             return S_OK;
143         }
144 
145         STDMETHODIMP
Drop(IDataObject * pDataObject,DWORD grfKeyState,POINTL pt,DWORD * pdwEffect)146         Drop(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) override
147         {
148             TRACE("(%p) object dropped on recycle bin, effect %u\n", this, *pdwEffect);
149 
150             FORMATETC fmt;
151             TRACE("(%p)->(DataObject=%p)\n", this, pDataObject);
152             InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL);
153 
154             /* Handle cfShellIDList Drop objects here, otherwise send the appropriate message to other software */
155             if (SUCCEEDED(pDataObject->QueryGetData(&fmt)))
156             {
157                 DWORD fMask = 0;
158 
159                 if ((grfKeyState & MK_SHIFT) == MK_SHIFT)
160                     fMask |= CMIC_MASK_SHIFT_DOWN;
161 
162                 _DoDeleteAsync(pDataObject, fMask);
163             }
164             else
165             {
166                 /*
167                  * TODO call SetData on the data object with format CFSTR_TARGETCLSID
168                  * set to the Recycle Bin's class identifier CLSID_RecycleBin.
169                  */
170             }
171             return S_OK;
172         }
173 
174         DECLARE_NOT_AGGREGATABLE(CRecyclerDropTarget)
175 
176         DECLARE_PROTECT_FINAL_CONSTRUCT()
177 
178         BEGIN_COM_MAP(CRecyclerDropTarget)
179         COM_INTERFACE_ENTRY_IID(IID_IDropTarget, IDropTarget)
180         END_COM_MAP()
181 };
182 
CRecyclerDropTarget_CreateInstance(REFIID riid,LPVOID * ppvOut)183 HRESULT CRecyclerDropTarget_CreateInstance(REFIID riid, LPVOID * ppvOut)
184 {
185     return ShellObjectCreator<CRecyclerDropTarget>(riid, ppvOut);
186 }
187