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 
12 CDeskLinkDropHandler::CDeskLinkDropHandler()
13 {
14     InterlockedIncrement(&g_ModuleRefCnt);
15 }
16 
17 CDeskLinkDropHandler::~CDeskLinkDropHandler()
18 {
19     InterlockedDecrement(&g_ModuleRefCnt);
20 }
21 
22 // IDropTarget
23 STDMETHODIMP
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
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 
44 STDMETHODIMP CDeskLinkDropHandler::DragLeave()
45 {
46     TRACE("(%p)\n", this);
47     return S_OK;
48 }
49 
50 STDMETHODIMP
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     FORMATETC fmt;
63     fmt.cfFormat = RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
64     fmt.ptd = NULL;
65     fmt.dwAspect = DVASPECT_CONTENT;
66     fmt.lindex = -1;
67     fmt.tymed = TYMED_HGLOBAL;
68 
69     WCHAR szDir[MAX_PATH], szDest[MAX_PATH], szSrc[MAX_PATH];
70     SHGetSpecialFolderPathW(NULL, szDir, CSIDL_DESKTOPDIRECTORY, FALSE);
71 
72     CComPtr<IShellFolder> pDesktop;
73     HRESULT hr = SHGetDesktopFolder(&pDesktop);
74     if (FAILED_UNEXPECTEDLY(hr))
75         return hr;
76 
77     STGMEDIUM medium;
78     hr = pDataObject->GetData(&fmt, &medium);
79     if (FAILED_UNEXPECTEDLY(hr))
80         return hr;
81 
82     LPIDA pida = reinterpret_cast<LPIDA>(GlobalLock(medium.hGlobal));
83     if (!pida)
84     {
85         ERR("Error locking global\n");
86         ReleaseStgMedium(&medium);
87         return E_FAIL;
88     }
89 
90     LPBYTE pb = reinterpret_cast<LPBYTE>(pida);
91     LPCITEMIDLIST pidlParent = reinterpret_cast<LPCITEMIDLIST>(pb + pida->aoffset[0]);
92     for (UINT i = 1; i <= pida->cidl; ++i)
93     {
94         LPCITEMIDLIST pidlChild = reinterpret_cast<LPCITEMIDLIST>(pb + pida->aoffset[i]);
95 
96         CComHeapPtr<ITEMIDLIST> pidl(ILCombine(pidlParent, pidlChild));
97         if (!pidl)
98         {
99             ERR("Out of memory\n");
100             break;
101         }
102 
103         StringCbCopyW(szDest, sizeof(szDest), szDir);
104         if (SHGetPathFromIDListW(pidl, szSrc))
105         {
106             CStringW strTitle;
107             strTitle.Format(IDS_SHORTCUT, PathFindFileNameW(szSrc));
108 
109             PathAppendW(szDest, strTitle);
110             PathRemoveExtensionW(szDest);
111             StringCbCatW(szDest, sizeof(szDest), L".lnk");
112 
113             hr = CreateShellLink(szDest, szSrc, NULL, NULL, NULL, NULL, -1, NULL);
114         }
115         else
116         {
117             STRRET strret;
118             hr = pDesktop->GetDisplayNameOf(pidl, SHGDN_INFOLDER, &strret);
119             if (FAILED_UNEXPECTEDLY(hr))
120                 break;
121 
122             hr = StrRetToBufW(&strret, pidl, szSrc, _countof(szSrc));
123             if (FAILED_UNEXPECTEDLY(hr))
124                 break;
125 
126             CStringW strTitle;
127             strTitle.Format(IDS_SHORTCUT, szSrc);
128 
129             PathAppendW(szDest, strTitle);
130             PathRemoveExtensionW(szDest);
131             StringCbCatW(szDest, sizeof(szDest), L".lnk");
132 
133             hr = CreateShellLink(szDest, NULL, pidl, NULL, NULL, NULL, -1, NULL);
134         }
135 
136         if (FAILED_UNEXPECTEDLY(hr))
137             break;
138     }
139 
140     GlobalUnlock(medium.hGlobal);
141     ReleaseStgMedium(&medium);
142 
143     return hr;
144 }
145 
146 // IPersistFile
147 STDMETHODIMP CDeskLinkDropHandler::GetCurFile(LPOLESTR *ppszFileName)
148 {
149     FIXME("(%p)\n", this);
150     return E_NOTIMPL;
151 }
152 
153 STDMETHODIMP CDeskLinkDropHandler::IsDirty()
154 {
155     FIXME("(%p)\n", this);
156     return E_NOTIMPL;
157 }
158 
159 STDMETHODIMP CDeskLinkDropHandler::Load(LPCOLESTR pszFileName, DWORD dwMode)
160 {
161     return S_OK;
162 }
163 
164 STDMETHODIMP CDeskLinkDropHandler::Save(LPCOLESTR pszFileName, BOOL fRemember)
165 {
166     FIXME("(%p)\n", this);
167     return E_NOTIMPL;
168 }
169 
170 STDMETHODIMP CDeskLinkDropHandler::SaveCompleted(LPCOLESTR pszFileName)
171 {
172     FIXME("(%p)\n", this);
173     return E_NOTIMPL;
174 }
175 
176 // IPersist
177 STDMETHODIMP CDeskLinkDropHandler::GetClassID(CLSID * lpClassId)
178 {
179     TRACE("(%p)\n", this);
180 
181     if (!lpClassId)
182     {
183         ERR("lpClassId is NULL\n");
184         return E_POINTER;
185     }
186 
187     *lpClassId = CLSID_DeskLinkDropHandler;
188 
189     return S_OK;
190 }
191