diff options
Diffstat (limited to 'src/wcautil/wcascript.cpp')
-rw-r--r-- | src/wcautil/wcascript.cpp | 447 |
1 files changed, 447 insertions, 0 deletions
diff --git a/src/wcautil/wcascript.cpp b/src/wcautil/wcascript.cpp new file mode 100644 index 00000000..b6629850 --- /dev/null +++ b/src/wcautil/wcascript.cpp | |||
@@ -0,0 +1,447 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | |||
5 | |||
6 | static HRESULT CaScriptFileName( | ||
7 | __in WCA_ACTION action, | ||
8 | __in WCA_CASCRIPT script, | ||
9 | __in BOOL fImpersonated, | ||
10 | __in_z LPCWSTR wzScriptKey, | ||
11 | __out LPWSTR* pwzScriptName | ||
12 | ); | ||
13 | |||
14 | |||
15 | /******************************************************************** | ||
16 | WcaCaScriptCreateKey() - creates a unique script key for this | ||
17 | CustomAction. | ||
18 | |||
19 | ********************************************************************/ | ||
20 | extern "C" HRESULT WIXAPI WcaCaScriptCreateKey( | ||
21 | __out LPWSTR* ppwzScriptKey | ||
22 | ) | ||
23 | { | ||
24 | AssertSz(WcaIsInitialized(), "WcaInitialize() should have been called before calling this function."); | ||
25 | HRESULT hr = S_OK; | ||
26 | |||
27 | hr = StrAllocStringAnsi(ppwzScriptKey, WcaGetLogName(), 0, CP_ACP); | ||
28 | ExitOnFailure(hr, "Failed to create script key."); | ||
29 | |||
30 | LExit: | ||
31 | return hr; | ||
32 | } | ||
33 | |||
34 | |||
35 | /******************************************************************** | ||
36 | WcaCaScriptCreate() - creates the appropriate script for this | ||
37 | CustomAction Script Key. | ||
38 | |||
39 | ********************************************************************/ | ||
40 | extern "C" HRESULT WIXAPI WcaCaScriptCreate( | ||
41 | __in WCA_ACTION action, | ||
42 | __in WCA_CASCRIPT script, | ||
43 | __in BOOL fImpersonated, | ||
44 | __in_z LPCWSTR wzScriptKey, | ||
45 | __in BOOL fAppend, | ||
46 | __out WCA_CASCRIPT_HANDLE* phScript | ||
47 | ) | ||
48 | { | ||
49 | HRESULT hr = S_OK; | ||
50 | LPWSTR pwzScriptPath = NULL; | ||
51 | HANDLE hScriptFile = INVALID_HANDLE_VALUE; | ||
52 | |||
53 | hr = CaScriptFileName(action, script, fImpersonated, wzScriptKey, &pwzScriptPath); | ||
54 | ExitOnFailure(hr, "Failed to calculate script file name."); | ||
55 | |||
56 | hScriptFile = ::CreateFileW(pwzScriptPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, fAppend ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); | ||
57 | if (INVALID_HANDLE_VALUE == hScriptFile) | ||
58 | { | ||
59 | ExitWithLastError(hr, "Failed to open CaScript: %ls", pwzScriptPath); | ||
60 | } | ||
61 | |||
62 | if (fAppend && INVALID_SET_FILE_POINTER == ::SetFilePointer(hScriptFile, 0, NULL, FILE_END)) | ||
63 | { | ||
64 | ExitWithLastError(hr, "Failed to seek to end of file."); | ||
65 | } | ||
66 | |||
67 | *phScript = static_cast<WCA_CASCRIPT_HANDLE>(MemAlloc(sizeof(WCA_CASCRIPT_STRUCT), TRUE)); | ||
68 | ExitOnNull(*phScript, hr, E_OUTOFMEMORY, "Failed to allocate space for cascript handle."); | ||
69 | |||
70 | (*phScript)->pwzScriptPath = pwzScriptPath; | ||
71 | pwzScriptPath = NULL; | ||
72 | (*phScript)->hScriptFile = hScriptFile; | ||
73 | hScriptFile = INVALID_HANDLE_VALUE; | ||
74 | |||
75 | LExit: | ||
76 | if (INVALID_HANDLE_VALUE != hScriptFile) | ||
77 | { | ||
78 | ::CloseHandle(hScriptFile); | ||
79 | } | ||
80 | |||
81 | ReleaseStr(pwzScriptPath); | ||
82 | return hr; | ||
83 | } | ||
84 | |||
85 | |||
86 | /******************************************************************** | ||
87 | WcaCaScriptOpen() - opens the appropriate script for this CustomAction | ||
88 | Script Key. | ||
89 | |||
90 | ********************************************************************/ | ||
91 | extern "C" HRESULT WIXAPI WcaCaScriptOpen( | ||
92 | __in WCA_ACTION action, | ||
93 | __in WCA_CASCRIPT script, | ||
94 | __in BOOL fImpersonated, | ||
95 | __in_z LPCWSTR wzScriptKey, | ||
96 | __out WCA_CASCRIPT_HANDLE* phScript | ||
97 | ) | ||
98 | { | ||
99 | HRESULT hr = S_OK; | ||
100 | LPWSTR pwzScriptPath = NULL; | ||
101 | HANDLE hScriptFile = INVALID_HANDLE_VALUE; | ||
102 | |||
103 | hr = CaScriptFileName(action, script, fImpersonated, wzScriptKey, &pwzScriptPath); | ||
104 | ExitOnFailure(hr, "Failed to calculate script file name."); | ||
105 | |||
106 | hScriptFile = ::CreateFileW(pwzScriptPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); | ||
107 | if (INVALID_HANDLE_VALUE == hScriptFile) | ||
108 | { | ||
109 | ExitWithLastError(hr, "Failed to open CaScript: %ls", pwzScriptPath); | ||
110 | } | ||
111 | |||
112 | *phScript = static_cast<WCA_CASCRIPT_HANDLE>(MemAlloc(sizeof(WCA_CASCRIPT_STRUCT), TRUE)); | ||
113 | ExitOnNull(*phScript, hr, E_OUTOFMEMORY, "Failed to allocate space for cascript handle."); | ||
114 | |||
115 | (*phScript)->pwzScriptPath = pwzScriptPath; | ||
116 | pwzScriptPath = NULL; | ||
117 | (*phScript)->hScriptFile = hScriptFile; | ||
118 | hScriptFile = INVALID_HANDLE_VALUE; | ||
119 | |||
120 | LExit: | ||
121 | if (INVALID_HANDLE_VALUE != hScriptFile) | ||
122 | { | ||
123 | ::CloseHandle(hScriptFile); | ||
124 | } | ||
125 | |||
126 | ReleaseStr(pwzScriptPath); | ||
127 | return hr; | ||
128 | } | ||
129 | |||
130 | |||
131 | /******************************************************************** | ||
132 | WcaCaScriptClose() - closes an open script handle. | ||
133 | |||
134 | ********************************************************************/ | ||
135 | extern "C" void WIXAPI WcaCaScriptClose( | ||
136 | __in_opt WCA_CASCRIPT_HANDLE hScript, | ||
137 | __in WCA_CASCRIPT_CLOSE closeOperation | ||
138 | ) | ||
139 | { | ||
140 | if (hScript) | ||
141 | { | ||
142 | if (INVALID_HANDLE_VALUE != hScript->hScriptFile) | ||
143 | { | ||
144 | ::CloseHandle(hScript->hScriptFile); | ||
145 | } | ||
146 | |||
147 | if (hScript->pwzScriptPath) | ||
148 | { | ||
149 | if (WCA_CASCRIPT_CLOSE_DELETE == closeOperation) | ||
150 | { | ||
151 | ::DeleteFileW(hScript->pwzScriptPath); | ||
152 | } | ||
153 | |||
154 | StrFree(hScript->pwzScriptPath); | ||
155 | } | ||
156 | |||
157 | MemFree(hScript); | ||
158 | } | ||
159 | } | ||
160 | |||
161 | |||
162 | /******************************************************************** | ||
163 | WcaCaScriptReadAsCustomActionData() - read the ca script into a format | ||
164 | that is useable by other CA data | ||
165 | functions. | ||
166 | |||
167 | ********************************************************************/ | ||
168 | extern "C" HRESULT WIXAPI WcaCaScriptReadAsCustomActionData( | ||
169 | __in WCA_CASCRIPT_HANDLE hScript, | ||
170 | __out LPWSTR* ppwzCustomActionData | ||
171 | ) | ||
172 | { | ||
173 | HRESULT hr = S_OK; | ||
174 | LARGE_INTEGER liScriptSize = { 0 }; | ||
175 | BYTE* pbData = NULL; | ||
176 | DWORD cbData = 0; | ||
177 | |||
178 | if (!::GetFileSizeEx(hScript->hScriptFile, &liScriptSize)) | ||
179 | { | ||
180 | ExitWithLastError(hr, "Failed to get size of ca script file."); | ||
181 | } | ||
182 | |||
183 | if (0 != liScriptSize.HighPart || 0 != (liScriptSize.LowPart % sizeof(WCHAR))) | ||
184 | { | ||
185 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | ||
186 | ExitOnRootFailure(hr, "Invalid data read from ca script."); | ||
187 | } | ||
188 | |||
189 | cbData = liScriptSize.LowPart; | ||
190 | if (cbData) | ||
191 | { | ||
192 | pbData = static_cast<BYTE*>(MemAlloc(cbData, TRUE)); | ||
193 | ExitOnNull(pbData, hr, E_OUTOFMEMORY, "Failed to allocate memory to read in ca script."); | ||
194 | |||
195 | if (INVALID_SET_FILE_POINTER == ::SetFilePointer(hScript->hScriptFile, 0, NULL, FILE_BEGIN)) | ||
196 | { | ||
197 | ExitWithLastError(hr, "Failed to reset to beginning of ca script."); | ||
198 | } | ||
199 | |||
200 | DWORD cbTotalRead = 0; | ||
201 | DWORD cbRead = 0; | ||
202 | do | ||
203 | { | ||
204 | if (!::ReadFile(hScript->hScriptFile, pbData + cbTotalRead, cbData - cbTotalRead, &cbRead, NULL)) | ||
205 | { | ||
206 | ExitWithLastError(hr, "Failed to read from ca script."); | ||
207 | } | ||
208 | |||
209 | cbTotalRead += cbRead; | ||
210 | } while (cbRead && cbTotalRead < cbData); | ||
211 | |||
212 | if (cbTotalRead != cbData) | ||
213 | { | ||
214 | hr = E_UNEXPECTED; | ||
215 | ExitOnFailure(hr, "Failed to completely read ca script."); | ||
216 | } | ||
217 | } | ||
218 | |||
219 | // Add one to the allocated space because the data stored in the script is not | ||
220 | // null terminated. After copying the memory over, we'll ensure the string is | ||
221 | // null terminated. | ||
222 | DWORD cchData = cbData / sizeof(WCHAR) + 1; | ||
223 | hr = StrAlloc(ppwzCustomActionData, cchData); | ||
224 | ExitOnFailure(hr, "Failed to copy ca script."); | ||
225 | |||
226 | if (cbData) | ||
227 | { | ||
228 | CopyMemory(*ppwzCustomActionData, pbData, cbData); | ||
229 | } | ||
230 | |||
231 | (*ppwzCustomActionData)[cchData - 1] = L'\0'; | ||
232 | |||
233 | LExit: | ||
234 | ReleaseMem(pbData); | ||
235 | return hr; | ||
236 | } | ||
237 | |||
238 | |||
239 | /******************************************************************** | ||
240 | WcaCaScriptWriteString() - writes a string to the ca script. | ||
241 | |||
242 | ********************************************************************/ | ||
243 | extern "C" HRESULT WIXAPI WcaCaScriptWriteString( | ||
244 | __in WCA_CASCRIPT_HANDLE hScript, | ||
245 | __in_z LPCWSTR wzValue | ||
246 | ) | ||
247 | { | ||
248 | HRESULT hr = S_OK; | ||
249 | DWORD cbFile = 0; | ||
250 | DWORD cbWrite = 0; | ||
251 | DWORD cbTotalWritten = 0; | ||
252 | WCHAR delim[] = { MAGIC_MULTISZ_DELIM }; // magic char followed by NULL terminator | ||
253 | |||
254 | cbFile = ::SetFilePointer(hScript->hScriptFile, 0, NULL, FILE_END); | ||
255 | if (INVALID_SET_FILE_POINTER == cbFile) | ||
256 | { | ||
257 | ExitWithLastError(hr, "Failed to move file pointer to end of file."); | ||
258 | } | ||
259 | |||
260 | // If there is existing data in the file, append on the magic delimeter | ||
261 | // before adding our new data on the end of the file. | ||
262 | if (0 < cbFile) | ||
263 | { | ||
264 | cbWrite = sizeof(delim); | ||
265 | cbTotalWritten = 0; | ||
266 | while (cbTotalWritten < cbWrite) | ||
267 | { | ||
268 | DWORD cbWritten = 0; | ||
269 | if (!::WriteFile(hScript->hScriptFile, reinterpret_cast<BYTE*>(delim) + cbTotalWritten, cbWrite - cbTotalWritten, &cbWritten, NULL)) | ||
270 | { | ||
271 | ExitWithLastError(hr, "Failed to write data to ca script."); | ||
272 | } | ||
273 | |||
274 | cbTotalWritten += cbWritten; | ||
275 | } | ||
276 | } | ||
277 | |||
278 | cbWrite = lstrlenW(wzValue) * sizeof(WCHAR); | ||
279 | cbTotalWritten = 0; | ||
280 | while (cbTotalWritten < cbWrite) | ||
281 | { | ||
282 | DWORD cbWritten = 0; | ||
283 | if (!::WriteFile(hScript->hScriptFile, reinterpret_cast<const BYTE*>(wzValue) + cbTotalWritten, cbWrite - cbTotalWritten, &cbWritten, NULL)) | ||
284 | { | ||
285 | ExitWithLastError(hr, "Failed to write data to ca script."); | ||
286 | } | ||
287 | |||
288 | cbTotalWritten += cbWritten; | ||
289 | } | ||
290 | |||
291 | LExit: | ||
292 | return hr; | ||
293 | } | ||
294 | |||
295 | |||
296 | /******************************************************************** | ||
297 | WcaCaScriptWriteNumber() - writes a number to the ca script. | ||
298 | |||
299 | ********************************************************************/ | ||
300 | extern "C" HRESULT WIXAPI WcaCaScriptWriteNumber( | ||
301 | __in WCA_CASCRIPT_HANDLE hScript, | ||
302 | __in DWORD dwValue | ||
303 | ) | ||
304 | { | ||
305 | HRESULT hr = S_OK; | ||
306 | WCHAR wzBuffer[13] = { 0 }; | ||
307 | |||
308 | hr = ::StringCchPrintfW(wzBuffer, countof(wzBuffer), L"%u", dwValue); | ||
309 | ExitOnFailure(hr, "Failed to convert number into string."); | ||
310 | |||
311 | hr = WcaCaScriptWriteString(hScript, wzBuffer); | ||
312 | ExitOnFailure(hr, "Failed to write number to script."); | ||
313 | |||
314 | LExit: | ||
315 | return hr; | ||
316 | } | ||
317 | |||
318 | |||
319 | /******************************************************************** | ||
320 | WcaCaScriptFlush() - best effort function to get script written to | ||
321 | disk. | ||
322 | |||
323 | ********************************************************************/ | ||
324 | extern "C" void WIXAPI WcaCaScriptFlush( | ||
325 | __in WCA_CASCRIPT_HANDLE hScript | ||
326 | ) | ||
327 | { | ||
328 | ::FlushFileBuffers(hScript->hScriptFile); | ||
329 | } | ||
330 | |||
331 | |||
332 | /******************************************************************** | ||
333 | WcaCaScriptCleanup() - best effort clean-up of any cascripts left | ||
334 | over from this install/uninstall. | ||
335 | |||
336 | ********************************************************************/ | ||
337 | extern "C" void WIXAPI WcaCaScriptCleanup( | ||
338 | __in_z LPCWSTR wzProductCode, | ||
339 | __in BOOL fImpersonated | ||
340 | ) | ||
341 | { | ||
342 | HRESULT hr = S_OK; | ||
343 | WCHAR wzTempPath[MAX_PATH]; | ||
344 | LPWSTR pwzWildCardPath = NULL; | ||
345 | WIN32_FIND_DATAW fd = { 0 }; | ||
346 | HANDLE hff = INVALID_HANDLE_VALUE; | ||
347 | LPWSTR pwzDeletePath = NULL; | ||
348 | |||
349 | if (fImpersonated) | ||
350 | { | ||
351 | if (!::GetTempPathW(countof(wzTempPath), wzTempPath)) | ||
352 | { | ||
353 | ExitWithLastError(hr, "Failed to get temp path."); | ||
354 | } | ||
355 | } | ||
356 | else | ||
357 | { | ||
358 | if (!::GetWindowsDirectoryW(wzTempPath, countof(wzTempPath))) | ||
359 | { | ||
360 | ExitWithLastError(hr, "Failed to get windows path."); | ||
361 | } | ||
362 | |||
363 | hr = ::StringCchCatW(wzTempPath, countof(wzTempPath), L"\\Installer\\"); | ||
364 | ExitOnFailure(hr, "Failed to concat Installer directory on windows path string."); | ||
365 | } | ||
366 | |||
367 | hr = StrAllocFormatted(&pwzWildCardPath, L"%swix%s.*.???", wzTempPath, wzProductCode); | ||
368 | ExitOnFailure(hr, "Failed to allocate wildcard path to ca scripts."); | ||
369 | |||
370 | hff = ::FindFirstFileW(pwzWildCardPath, &fd); | ||
371 | if (INVALID_HANDLE_VALUE == hff) | ||
372 | { | ||
373 | ExitWithLastError(hr, "Failed to find files with pattern: %ls", pwzWildCardPath); | ||
374 | } | ||
375 | |||
376 | do | ||
377 | { | ||
378 | hr = StrAllocFormatted(&pwzDeletePath, L"%s%s", wzTempPath, fd.cFileName); | ||
379 | if (SUCCEEDED(hr)) | ||
380 | { | ||
381 | if (!::DeleteFileW(pwzDeletePath)) | ||
382 | { | ||
383 | DWORD er = ::GetLastError(); | ||
384 | WcaLog(LOGMSG_VERBOSE, "Failed to clean up CAScript file: %ls, er: %d", fd.cFileName, er); | ||
385 | } | ||
386 | } | ||
387 | else | ||
388 | { | ||
389 | WcaLog(LOGMSG_VERBOSE, "Failed to allocate path to clean up CAScript file: %ls, hr: 0x%x", fd.cFileName, hr); | ||
390 | } | ||
391 | } while(::FindNextFileW(hff, &fd)); | ||
392 | |||
393 | LExit: | ||
394 | if (INVALID_HANDLE_VALUE == hff) | ||
395 | { | ||
396 | ::FindClose(hff); | ||
397 | } | ||
398 | |||
399 | ReleaseStr(pwzDeletePath); | ||
400 | ReleaseStr(pwzWildCardPath); | ||
401 | return; | ||
402 | } | ||
403 | |||
404 | |||
405 | static HRESULT CaScriptFileName( | ||
406 | __in WCA_ACTION action, | ||
407 | __in WCA_CASCRIPT script, | ||
408 | __in BOOL fImpersonated, | ||
409 | __in_z LPCWSTR wzScriptKey, | ||
410 | __out LPWSTR* ppwzScriptName | ||
411 | ) | ||
412 | { | ||
413 | HRESULT hr = S_OK; | ||
414 | WCHAR wzTempPath[MAX_PATH]; | ||
415 | LPWSTR pwzProductCode = NULL; | ||
416 | WCHAR chInstallOrUninstall = action == WCA_ACTION_INSTALL ? L'i' : L'u'; | ||
417 | WCHAR chScheduledOrRollback = script == WCA_CASCRIPT_SCHEDULED ? L's' : L'r'; | ||
418 | WCHAR chUserOrMachine = fImpersonated ? L'u' : L'm'; | ||
419 | |||
420 | if (fImpersonated) | ||
421 | { | ||
422 | if (!::GetTempPathW(countof(wzTempPath), wzTempPath)) | ||
423 | { | ||
424 | ExitWithLastError(hr, "Failed to get temp path."); | ||
425 | } | ||
426 | } | ||
427 | else | ||
428 | { | ||
429 | if (!::GetWindowsDirectoryW(wzTempPath, countof(wzTempPath))) | ||
430 | { | ||
431 | ExitWithLastError(hr, "Failed to get windows path."); | ||
432 | } | ||
433 | |||
434 | hr = ::StringCchCatW(wzTempPath, countof(wzTempPath), L"\\Installer\\"); | ||
435 | ExitOnFailure(hr, "Failed to concat Installer directory on windows path string."); | ||
436 | } | ||
437 | |||
438 | hr = WcaGetProperty(L"ProductCode", &pwzProductCode); | ||
439 | ExitOnFailure(hr, "Failed to get ProductCode."); | ||
440 | |||
441 | hr = StrAllocFormatted(ppwzScriptName, L"%swix%s.%s.%c%c%c", wzTempPath, pwzProductCode, wzScriptKey, chScheduledOrRollback, chUserOrMachine, chInstallOrUninstall); | ||
442 | ExitOnFailure(hr, "Failed to allocate path to ca script."); | ||
443 | |||
444 | LExit: | ||
445 | ReleaseStr(pwzProductCode); | ||
446 | return hr; | ||
447 | } | ||