1 /*
2  * PROJECT:     sendmail
3  * LICENSE:     LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+)
4  * PURPOSE:     DeskLink implementation
5  * COPYRIGHT:   Copyright 2019 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
6  */
7 
8 #include "precomp.hpp"
9 
10 WINE_DEFAULT_DEBUG_CHANNEL(sendmail);
11 
CDeskLinkDropHandler()12 CDeskLinkDropHandler::CDeskLinkDropHandler()
13 {
14     InterlockedIncrement(&g_ModuleRefCnt);
15 }
16 
~CDeskLinkDropHandler()17 CDeskLinkDropHandler::~CDeskLinkDropHandler()
18 {
19     InterlockedDecrement(&g_ModuleRefCnt);
20 }
21 
22 // IDropTarget
23 STDMETHODIMP
DragEnter(IDataObject * pDataObject,DWORD dwKeyState,POINTL pt,DWORD * pdwEffect)24 CDeskLinkDropHandler::DragEnter(IDataObject *pDataObject, DWORD dwKeyState,
25                                 POINTL pt, DWORD *pdwEffect)
26 {
27     TRACE("(%p)\n", this);
28 
29     *pdwEffect &= DROPEFFECT_LINK;
30 
31     return S_OK;
32 }
33 
34 STDMETHODIMP
DragOver(DWORD dwKeyState,POINTL pt,DWORD * pdwEffect)35 CDeskLinkDropHandler::DragOver(DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
36 {
37     TRACE("(%p)\n", this);
38 
39     *pdwEffect &= DROPEFFECT_LINK;
40 
41     return S_OK;
42 }
43 
DragLeave()44 STDMETHODIMP CDeskLinkDropHandler::DragLeave()
45 {
46     TRACE("(%p)\n", this);
47     return S_OK;
48 }
49 
50 STDMETHODIMP
Drop(IDataObject * pDataObject,DWORD dwKeyState,POINTL pt,DWORD * pdwEffect)51 CDeskLinkDropHandler::Drop(IDataObject *pDataObject, DWORD dwKeyState,
52                            POINTL pt, DWORD *pdwEffect)
53 {
54     TRACE("(%p)\n", this);
55 
56     if (!pDataObject)
57     {
58         ERR("pDataObject is NULL\n");
59         return E_POINTER;
60     }
61 
62     WCHAR szDir[MAX_PATH], szDest[MAX_PATH], szSrc[MAX_PATH];
63     SHGetSpecialFolderPathW(NULL, szDir, CSIDL_DESKTOPDIRECTORY, FALSE);
64 
65     CComPtr<IShellFolder> pDesktop;
66     HRESULT hr = SHGetDesktopFolder(&pDesktop);
67     if (FAILED_UNEXPECTEDLY(hr))
68         return hr;
69 
70     CDataObjectHIDA pida(pDataObject);
71     if (FAILED_UNEXPECTEDLY(pida.hr()))
72         return pida.hr();
73 
74     LPCITEMIDLIST pidlParent = HIDA_GetPIDLFolder(pida);
75     for (UINT i = 0; i < pida->cidl; ++i)
76     {
77         LPCITEMIDLIST pidlChild = HIDA_GetPIDLItem(pida, i);
78 
79         CComHeapPtr<ITEMIDLIST> pidl(ILCombine(pidlParent, pidlChild));
80         if (!pidl)
81         {
82             ERR("Out of memory\n");
83             break;
84         }
85 
86         StringCbCopyW(szDest, sizeof(szDest), szDir);
87         if (SHGetPathFromIDListW(pidl, szSrc))
88         {
89             LPCWSTR pszSourceExt;
90             BOOL bIsLink;
91 
92             pszSourceExt = PathFindExtensionW(szSrc);
93             bIsLink = ((_wcsicmp(pszSourceExt, L".lnk") == 0) ||
94                        (_wcsicmp(pszSourceExt, L".url") == 0));
95 
96             if (bIsLink)
97             {
98                 PathAppendW(szDest, PathFindFileNameW(szSrc));
99             }
100             else
101             {
102                 CStringW strTitle;
103                 strTitle.Format(IDS_SHORTCUT, PathFindFileNameW(szSrc));
104 
105                 PathAppendW(szDest, strTitle);
106                 PathRemoveExtensionW(szDest);
107                 StringCbCatW(szDest, sizeof(szDest), L".lnk");
108             }
109 
110             if (PathFileExistsW(szDest))
111             {
112                 CStringW strName(PathFindFileNameW(szDest));
113                 PathYetAnotherMakeUniqueName(szDest, szDir, NULL, strName);
114             }
115 
116             if (bIsLink)
117             {
118                 hr = (CopyFileW(szSrc, szDest, TRUE) ? S_OK : E_FAIL);
119             }
120             else if (PathIsDirectoryW(szSrc) || (_wcsicmp(pszSourceExt, L".zip") == 0))
121             {
122                 hr = CreateShellLink(szDest, szSrc, NULL, NULL, NULL, NULL, -1, NULL);
123             }
124             else
125             {
126                 /* Set default working directory for the shortcut */
127                 CStringW strWorkingDir(szSrc);
128                 PathRemoveFileSpecW(strWorkingDir.GetBuffer());
129 
130                 hr = CreateShellLink(szDest, szSrc, NULL, NULL, strWorkingDir, NULL, -1, NULL);
131             }
132         }
133         else
134         {
135             STRRET strret;
136             hr = pDesktop->GetDisplayNameOf(pidl, SHGDN_INFOLDER, &strret);
137             if (FAILED_UNEXPECTEDLY(hr))
138                 break;
139 
140             hr = StrRetToBufW(&strret, pidl, szSrc, _countof(szSrc));
141             if (FAILED_UNEXPECTEDLY(hr))
142                 break;
143 
144             CStringW strTitle;
145             strTitle.Format(IDS_SHORTCUT, szSrc);
146 
147             PathAppendW(szDest, strTitle);
148             PathRemoveExtensionW(szDest);
149             StringCbCatW(szDest, sizeof(szDest), L".lnk");
150 
151             hr = CreateShellLink(szDest, NULL, pidl, NULL, NULL, NULL, -1, NULL);
152         }
153 
154         if (FAILED_UNEXPECTEDLY(hr))
155             break;
156     }
157 
158     return hr;
159 }
160 
161 // IPersistFile
GetCurFile(LPOLESTR * ppszFileName)162 STDMETHODIMP CDeskLinkDropHandler::GetCurFile(LPOLESTR *ppszFileName)
163 {
164     FIXME("(%p)\n", this);
165     return E_NOTIMPL;
166 }
167 
IsDirty()168 STDMETHODIMP CDeskLinkDropHandler::IsDirty()
169 {
170     FIXME("(%p)\n", this);
171     return E_NOTIMPL;
172 }
173 
Load(LPCOLESTR pszFileName,DWORD dwMode)174 STDMETHODIMP CDeskLinkDropHandler::Load(LPCOLESTR pszFileName, DWORD dwMode)
175 {
176     return S_OK;
177 }
178 
Save(LPCOLESTR pszFileName,BOOL fRemember)179 STDMETHODIMP CDeskLinkDropHandler::Save(LPCOLESTR pszFileName, BOOL fRemember)
180 {
181     FIXME("(%p)\n", this);
182     return E_NOTIMPL;
183 }
184 
SaveCompleted(LPCOLESTR pszFileName)185 STDMETHODIMP CDeskLinkDropHandler::SaveCompleted(LPCOLESTR pszFileName)
186 {
187     FIXME("(%p)\n", this);
188     return E_NOTIMPL;
189 }
190 
191 // IPersist
GetClassID(CLSID * lpClassId)192 STDMETHODIMP CDeskLinkDropHandler::GetClassID(CLSID * lpClassId)
193 {
194     TRACE("(%p)\n", this);
195 
196     if (!lpClassId)
197     {
198         ERR("lpClassId is NULL\n");
199         return E_POINTER;
200     }
201 
202     *lpClassId = CLSID_DeskLinkDropHandler;
203 
204     return S_OK;
205 }
206