MSI: Fix possible use of unintialized variable
[gpg4win.git] / src / desktopshellrun.cpp
1 /* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik
2  * Copyright (C) 2016 Intevation GmbH
3  * Software engineering by Intevation GmbH
4  *
5  * This file is Free Software under the GNU GPL (v>=2)
6  * and comes with ABSOLUTELY NO WARRANTY!
7  */
8
9 #include <windows.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <initguid.h>
13 #include <mstask.h>
14 #include <wchar.h>
15 #include <ole2.h>
16 #include <shldisp.h>
17 #include <shobjidl.h>
18 #include <exdisp.h>
19 #include <shlguid.h>
20
21 #include "exdll.h"
22 #ifndef INITGUID
23 #define INITGUID
24 #endif
25
26 /* Some declarations missing in mingw-w64 3.1.0 taken from msdn */
27 #if ! defined (__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 5
28 __CRT_UUID_DECL(IShellWindows, 0x85CB6900, 0x4D95, 0x11CF,
29                 0x96, 0x0C, 0x00, 0x80, 0xC7, 0xF4, 0xEE, 0x85);
30
31 DEFINE_GUID(IID_IShellWindows,
32             0x85CB6900, 0x4D95, 0x11CF,
33             0x96, 0x0C, 0x00, 0x80, 0xC7, 0xF4, 0xEE, 0x85);
34 DEFINE_GUID(CLSID_ShellWindows,
35             0x9BA05972, 0xF6A8, 0x11CF,
36             0xA4, 0x42, 0x00, 0xA0, 0xC9, 0x0A, 0x8F, 0x39);
37
38
39 __CRT_UUID_DECL(IShellDispatch2, 0xA4C6892C, 0x3BA9, 0x11d2,
40                 0x9D, 0xEA, 0x00, 0xC0, 0x4F, 0xB1, 0x61, 0x62);
41 __CRT_UUID_DECL(IShellFolderViewDual,  0xe7a1af80, 0x4d96,
42                 0x11cf, 0x96, 0x0c, 0x00, 0x80, 0xc7, 0xf4, 0xee, 0x85);
43 #endif
44
45 #ifndef SWC_DESKTOP /* Will probably be addedd in future mingw */
46 #define SWC_DESKTOP 0x00000008
47 /* from http://msdn.microsoft.com/en-us/library/windows/desktop/cc836581%28v=vs.85%29.aspx */
48 #endif
49
50 #define UNUSED(x) (void)(x)
51
52 /** @brief the actual execuation call on the shell dispatcher
53  *
54  * @param[in] disp The shell dispatcher to use for shell execute.
55  * @param[in] fName The file that should be exectued.
56  * @param[in] param Optinal parameters to add.
57  *
58  * @returns true on success.
59  */
60 static bool
61 shellexecute(IShellDispatch2 *disp, wchar_t *fName, wchar_t *param)
62 {
63   BSTR bName = NULL,
64        bParam = NULL,
65        bDir = NULL,
66        bOp = NULL;
67   VARIANT vParams[4];
68   HRESULT hr;
69
70   if (!fName || !disp)
71     {
72       ERRORPRINTF ("Invalid call to shellexecute.");
73       return false;
74     }
75
76   bName = SysAllocString(fName);
77   bParam = SysAllocString(param ? param : L"");
78   bDir = SysAllocString(L"");
79   bOp = SysAllocString(L"");
80
81   if (!bName || !bParam || !bDir || !bOp)
82     {
83       /* Out of memory */
84       ERRORPRINTF ("Failed to allocate bstr values ");
85       return false;
86     }
87
88   vParams[0].vt = VT_BSTR;
89   vParams[0].bstrVal = bParam;
90   vParams[1].vt = VT_BSTR;
91   vParams[1].bstrVal = bDir;
92   vParams[2].vt = VT_BSTR;
93   vParams[2].bstrVal = bOp;
94   vParams[3].vt = VT_INT;
95   vParams[3].intVal = SW_SHOWNORMAL;
96
97   hr = disp->ShellExecute(bName, vParams[0], vParams[1], vParams[2], vParams[3]);
98
99   SysFreeString(bName);
100   SysFreeString(bParam);
101   SysFreeString(bOp);
102   SysFreeString(bDir);
103
104   if (FAILED(hr))
105     {
106       ERRORPRINTF ("Failed to execute.");
107       return false;
108     }
109   return true;
110 }
111
112 #ifdef __cplusplus
113 extern "C" {
114 #endif
115
116 /** @brief Execute a command with the current running shell.
117  *
118  * This function is intended to be called when you want to
119  * make sure that your application is not executed with higher
120  * privileges then the normal desktop session.
121  *
122  * The code is based on the idea:
123  * http://blogs.msdn.com/b/oldnewthing/archive/2013/11/18/10468726.aspx
124  *
125  * The function signature is explained by NSIS.
126  */
127 void __declspec(dllexport) __cdecl DesktopShellRun(HWND hwndParent,
128                                                    int string_size,
129                                                    char *variables,
130                                                    stack_t **stacktop)
131 {
132   UNUSED(hwndParent);
133   UNUSED(string_size);
134   HRESULT hr;
135   wchar_t *wbuf = NULL;
136   IShellWindows *shellWindows = NULL;
137   IShellBrowser *shellBrowser = NULL;
138   IShellView *shellView = NULL;
139   IShellFolderViewDual *folderView = NULL;
140   IShellDispatch2 *shellDispatch = NULL;
141   IServiceProvider *serviceProv = NULL;
142   HWND hwnd;
143   IDispatch *disp = NULL,
144              *bgDisp = NULL,
145               *sDisp = NULL;
146   VARIANT vEmpty = {};
147
148   if (!stacktop || !*stacktop || !(*stacktop)->text)
149     {
150       ERRORPRINTF ("Invalid call to exec :");
151       return;
152     }
153
154   /* Initialize com ctx */
155   hr = CoInitialize(NULL);
156   if(FAILED(hr))
157     {
158       ERRORPRINTF ("CoInitializeEx failed. error = 0x%lx.", hr);
159       return;
160     }
161
162   /* Get the shell interface */
163   hr = CoCreateInstance(CLSID_ShellWindows,
164                         NULL, CLSCTX_LOCAL_SERVER,
165                         IID_PPV_ARGS(&shellWindows));
166   if (FAILED(hr))
167     {
168       ERRORPRINTF ("Failed to get shell interface.");
169       goto done;
170     }
171
172   /* Get the desktop shell window */
173   hr = shellWindows->FindWindowSW(&vEmpty,
174                                   &vEmpty,
175                                   SWC_DESKTOP,
176                                   (long*)&hwnd,
177                                   SWFO_NEEDDISPATCH,
178                                   &disp);
179   if (FAILED(hr))
180     {
181       ERRORPRINTF ("Failed to find the desktop dispatcher.");
182       goto done;
183     }
184
185   hr = disp->QueryInterface(IID_PPV_ARGS(&serviceProv));
186
187   if (FAILED(hr))
188     {
189       ERRORPRINTF ("Failed to get the service provider.");
190       goto done;
191     }
192
193   /* Get the shell browser */
194   hr = serviceProv->QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&shellBrowser));
195   if (FAILED(hr))
196     {
197       ERRORPRINTF ("Failed to find the top level browser.");
198       goto done;
199     }
200
201   hr = shellBrowser->QueryActiveShellView(&shellView);
202
203   if (FAILED(hr))
204     {
205       ERRORPRINTF ("Failed to find the active view.");
206       goto done;
207     }
208
209   hr = shellView->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&bgDisp));
210
211   if (FAILED(hr))
212     {
213       ERRORPRINTF ("Failed to get the views background.");
214       goto done;
215     }
216
217   hr = bgDisp->QueryInterface(IID_PPV_ARGS(&folderView));
218
219   if (FAILED(hr))
220     {
221       ERRORPRINTF ("Failed to get the folder view.");
222       goto done;
223     }
224
225
226   hr = folderView->get_Application(&sDisp);
227
228   if (FAILED(hr))
229     {
230       ERRORPRINTF ("Failed to get the shell dispatch.");
231       goto done;
232     }
233
234   hr = sDisp->QueryInterface(IID_PPV_ARGS(&shellDispatch));
235
236   if (FAILED(hr))
237     {
238       ERRORPRINTF ("Failed to get the shell dispatch interface.");
239       goto done;
240     }
241
242   /* For unicodensis this has to be utf8 to wchar */
243
244   wbuf = acp_to_wchar((*stacktop)->text, strlen((*stacktop)->text));
245   if (!wbuf)
246     {
247       ERRORPRINTF ("Failed to convert argument to wchar. error = 0x%lx.", hr);
248       goto done;
249     }
250
251   if (!shellexecute(shellDispatch, wbuf, NULL))
252     {
253       ERRORPRINTF ("Failed to execute.");
254     }
255   if (wbuf)
256     free(wbuf);
257 done:
258   if (folderView)
259     {
260       folderView->Release();
261     }
262   if (disp)
263     {
264       disp->Release();
265     }
266   if (shellBrowser)
267     {
268       shellBrowser->Release();
269     }
270   if (shellWindows)
271     {
272       shellWindows->Release();
273     }
274   if (shellView)
275     {
276       shellView->Release();
277     }
278   if (sDisp)
279     {
280       sDisp->Release();
281     }
282   if (shellDispatch)
283     {
284       shellDispatch->Release();
285     }
286   if (serviceProv)
287     {
288       serviceProv->Release();
289     }
290   CoUninitialize();
291   return;
292 }
293
294 #ifdef __cplusplus
295 }
296 #endif